I am attempting to implement a simply typed lambda calculus type checker. When running sanity tests I tried typing (S K K) and my type checker throws this error:
TypeMismatch {firstType = t -> t, secondType = t -> t -> t}
The offending term is clearly the (S K K)
(\x:t -> t -> t.\y:t -> t.\z:t.x z (y z)) (\x:t.\y:t.x) (\\x:t.\y:t.x)
I think the problem arises from a lack of polymorphism because when I type check this haskell code it works fine:
k x y = x
s x y z = x z (y z)
test = s k k -- type checks
but if I specialize the type:
k :: () -> () -> ()
k x y = x
s :: (() -> () -> ()) -> (() -> ()) -> () -> ()
s x y z = x z (y z)
test = s k k -- doesn't type check
Just for reference my type system is a simple as it gets:
data Type = T | TArr Type Type
I will steal the ideas from a previous answer of mine to show how to ask ghci your question. But first I am going to reformulate your question slightly.
In Haskell, we have
and the question we want to ask is "What do these types look like after type-checking
s k k
?". More to the point, if we rewrite them with distinct unification variables,then the question becomes a unification one: we are trying to unify the type of
s
with the type it's being used at -- namely(d -> e -> d) -> (f -> g -> f) -> h
. Now that we have a unification question in hand, we can ask in the format showed in my other answer:And now we can see why your version doesn't work: in your version, you've instantiated all polymorphic variables to the base type
T
; but sinceb ~ g -> f
,e ~ g -> f
, andh ~ f -> f
are manifestly arrow types, that certainly isn't going to work! However, any choices forf
andg
will work if we respect the substitution above; in particular if we choosef ~ T
andg ~ T
, then we have