g | x | w | all
Bytes Lang Time Link
nanScala 3241027T133358Z138 Aspe
073Haskell + hgl241110T193912ZWheat Wi
040Prolog SWI241027T154756ZUnrelate
108OCaml241027T083208ZMerlin04
182Python3241024T185358ZAjax1234
085Python 3241024T231629Zapilat
038oK241024T175055Zzgrep
051JavaScript Node.js241023T065738Zl4m2
040Charcoal241023T234253ZNeil
199Retina 0.8.2241023T065105ZNeil
052JavaScript Node.js241023T070624Ztsh

Scala 3, 469 323 bytes

Saved 146 bytes thanks to @noodle person


Golfed version. Attempt This Online!

def f(a:Any,b:Any):Boolean=(a,b)match{case(0,b)=>b==0case(_,0)=>1<2case(x:List[_],y:List[_])=>if(x.isEmpty||y.isEmpty)false
else{val u=x.tail++y.tail
List((List(List(0,2),List(1,3)),1),(List(List(2,0),List(1,3)),2)).exists{case(r,t)=>t==x(0)&&t==y(0)    &&r.forall{p=>try{f(u(p(0)),u(p(1)))}catch{case _=>false}}}}case _=>1>2}

Ungolfed version. Attempt This Online!

object Main {
  // Helper type for better type safety and pattern matching
  type NestedList = Any

  def f(a: NestedList, b: NestedList): Boolean = {
    (a, b) match {
      // Base cases
      case (0, b) => b == 0
      case (_, 0) => true
      
      // Main recursive case for lists
      case (aList: List[_], bList: List[_]) =>
        if (aList.isEmpty || bList.isEmpty) return false
        
        // Define conditions similar to Python version
        val conditions: List[(List[List[Int]], Int)] = List(
          (List(List(0, 2), List(1, 3)), 1),
          (List(List(2, 0), List(1, 3)), 2)
        )
        
        // Get first elements and remaining lists
        val (aHead, aTail) = (aList.head, aList.tail)
        val (bHead, bTail) = (bList.head, bList.tail)
        
        // Combine remaining elements
        val U = aTail ++ bTail
        
        // Check conditions
        conditions.exists { case (r, t) =>
          t == aHead && t == bHead && {
            // Check all pairs of conditions
            r.forall { pair =>
              try {
                val x = pair(0)
                val y = pair(1)
                val ux = U(x)
                val uy = U(y)
                f(ux, uy)
              } catch {
                case _: Exception => false
              }
            }
          }
        }
        
      // Any other case returns false
      case _ => false
    }
  }

  def main(args: Array[String]): Unit = {
    // Convert Python nested lists to Scala
    println(f(0, 0))
    println(f(List(2, 0, List(1, 0, 0)), List(2, List(1, 0, 0), 0)))
    println(f(List(1, List(2, 0, 0), List(1, 0, 0)), List(1, 0, 0)))
    println(f(List(2, 0, List(2, 0, List(2, 0, 0))), List(2, 0, List(2, 0, 0))))
    println(f(0, List(2, 0, 0)))
    println(f(List(2, 0, 0), List(1, 0, 0)))
    println(f(List(2, List(1, 0, 0), 0), List(2, 0, 0)))
    println(f(List(1, 0, List(1, 0, 0)), List(1, List(1, 0, 0), 0)))
    println(f(List(1, List(2, List(1, 0, 0), 0), 0), List(1, List(1, 0, 0), 0)))
  }
}

Haskell + hgl, 73 bytes

_#Pu _=T
Fe(Co(s,[a,c]))#Fe(Co(z,[b,d]))|s,z=a#b<>c#d|s==z=b#a<>c#d
_#_=B

Attempt This Online!

Takes input as a:

Free (Comp ((,) Bool) List) ()

Which is basically a ragged list of ()s where every layer is labelled true (for pairs) or false (for functions).

Reflection

I'm pretty disappointed in this. This was a chance to bring out the more structural side of hgl and it is far to long for my liking.

In my ideal world the type I would be using would be:

Free (Comp (W Either) Vec2) ()

Not sure this could ever be competitive with a semantically poorer type, but I think it's worth striving for.

Some improvements I would like to see:

Prolog (SWI), 40 bytes

_+t.
A*B+C*D:-A+C,B+D.
A/B+C/D:-C+A,B+D.

Try it online!

Can't get much more to the point than this! Represents \$\top\$ as the atom t, \$S \times T\$ as the term S * T, and \$S \rightarrow T\$ as the term S / T; outputs through success or failure of the predicate +/2.

Each line is a direct translation of the three bullet points in the spec.

OCaml, 108 bytes

type t=T|D of bool*t*t
let rec h=function D(g,a,b),D(j,c,d)->g=j&&h(if g then c,a else a,c)&&h(b,d)|_,b->b=T

Try it online! (includes test cases)

Input to the function h is a tuple of two values of type t:

Python3, 182 bytes

def f(a,b):
 if a==0:return b==0
 if b==0:return 1
 for r,T in[([[0,2],[1,3]],1),([[2,0],[1,3]],2)]:
  U=a[1:]+b[1:]
  if T==a[0]and T==b[0]and all(f(U[X],U[Y])for X,Y in r):return 1

Try it online!

Python 3, 85 bytes

f=lambda s,t:t==0if t==0 or s==0else(t[0]==s[0])&f(s[2],t[2])&f(*[s[1],t[1]][::s[0]])

Try it online!

Uses 0 for Top, [1,S,T] for \$S \times T\$, [-1,S,T] for \$S \to T\$.

oK, 38 bytes

{$[*x&x=y;&/o.'(.*x;_)@'1_+(x;y);*~y]}

Try it online!

The input is encoded as follows, whether or not it's reasonable depends on how charitable you're feeling:

Output is 1 for true and 0 for false.

Translation:

{                                    } /function(x, y):
      x=y                              /  ans = (x == y)    // element-wise
    x&                                 /  ans = min(x, ans) // element-wise
   *                                   /  ans = first(ans)
 $[      ;                      ;   ]  /  if ans:
                           (x;y)       /    ans = [x, y]
                          +            /    ans = transpose(ans)
                        1_             /    ans = drop_first(ans)
                .*x                    /    f1 = eval(first(x))
                                       /    // eval interprets int as char:
                                       /    //    95 → "_" → floor
                                       /    //   124 → "|" → reverse
                    _                  /    f2 = floor
               (   ; )@'               /    ans = map(apply, zip([f1, f2], ans))
            o                          /    o = this function
             .'                        /    ans = map(uncurry, zip([o, o, …], ans))
          &/                           /    return fold(min, ans)
                                       /  else:
                                 *~y   /    return first(not y)

Explanation:


A similar 38-byte solution:

{$[*x&x=y;&/o.'(^;.*x;_)@'+(x;y);*~y]}

And if the input is a two-element array, as opposed to two arguments:

{$[**x&*=/x;&/o'(.**x;_)@'1_+x;**~|x]}

JavaScript (Node.js), 51 bytes

f=(x,y)=>y?x&&f(x.b,y.b)&f(x.a,y.a)+f(y.A,x.A):x>=x

Try it online!

{A:_,b:_} as arrow, {a:_,b:_} as mult,

Charcoal, 40 bytes

¹⊞υAFυF…§ι¹¦¹¿κ¿⁻κ§§ι⁰¦⁰⎚F²⊞υE⎇∧λ⊖κ⮌ιι⊟μ

Try it online! Link is to verbose version of code. Takes input as a list of the two types and uses [0] for Top and outputs a Charcoal boolean, i.e. 1 for subtype, nothing if not. Explanation:

¹

Assume that the first input element is a subtype of the second.

⊞υAFυ

Start by considering the two input types.

F…§ι¹¦¹

Get the kind of the second type.

¿κ

If the second type is not Top, then...

¿⁻κ§§ι⁰¦⁰⎚

... if the two types are different then they are not subtypes, otherwise...

F²⊞υE⎇∧λ⊖κ⮌ιι⊟μ

... take the two pairs of inner types, reverse the first pair if necessary, and save both pairs for further processing.

Retina 0.8.2, 219 199 bytes

^({(({)|(?<-3>})|\w)+)p((({)|(?<-6>})|\w)+},)({(({)|(?<-9>})|\w)+)p(?(9)^)
$7x$4$1x
^{((({)|(?<-3>})|\w)+)x((({)|(?<-6>})|\w)+)},{((({)|(?<-9>})|\w)+)x((({)|(?<-12>})|\w)+)}$
$1,$7¶$4,$10
+%)A`,T$
^$

Try it online! Link includes test cases. Explanation:

^({(({)|(?<-3>})|\w)+)p((({)|(?<-6>})|\w)+},)({(({)|(?<-9>})|\w)+)p(?(9)^)
$7x$4$1x

Match {S'pT},{SpT'} and turn it into {SxT},{S'xT'}.

^{((({)|(?<-3>})|\w)+)x((({)|(?<-6>})|\w)+)},{((({)|(?<-9>})|\w)+)x((({)|(?<-12>})|\w)+)}$
$1,$7¶$4,$10

Match {SxT},{S'xT'} and split it into S,S' and T,T'.

+%)A`,T$

Delete any conditions where the RHS is T and repeatedly process any remaining conditions.

^$

Check that there are no left-over conditions.

The input format is as follows:

JavaScript (Node.js), 52 bytes

f=([a,b,c],[x,y,z])=>~x?a-x||f(c,z)|f(a?y:b,a?b:y):0

Try it online!

Use [-1] for \$\mathbf{Top}\$, [0,S,T] for \$S\times T\$, [1,S,T] for \$S\to T\$.