This is a bit esoteric, but maddening. In an answer to another question, I noted that in this entirely valid program
poo :: String -> a -> a
poo _ = id
qoo :: (a -> a) -> String
qoo _ = ""
roo :: String -> String
roo = qoo . poo
the type variable a is neither solved nor generalized in the process of checking roo. I’m wondering what happens in the translation to GHC’s core language, a Church-style variant of System F. Let me spell things out longhand, with explicit type lambdas /\ and type applications @.
poo :: forall a. [Char] -> a -> a
poo = /\ a -> \ s x -> id @ a
qoo :: forall a. (a -> a) -> [Char]
qoo = /\ a -> \ f -> [] @ Char
roo :: [Char] -> [Char]
roo = (.) @ [Char] @ (? -> ?) @ [Char] (qoo @ ?) (poo @ ?)
What on earth goes in the ? places? How does roo become a valid core term? Or do we really get a mysterious vacuous quantifier, despite what the type signature says?
roo :: forall a. [Char] -> [Char]
roo = /\ a -> ...
I’ve just checked that
roo :: forall . String -> String
roo = qoo . poo
goes through ok, which may or may not mean that the thing typechecks with no extra quantification.
What’s happening down there?
Here’s the core generated by GHC (after adding some
NOINLINEpragmas).It seems
GHC.Prim.Anyis used for the polymorphic type.From the docs (emphasis mine):
It makes sense to have such a type to insert in place of un-constrained types, as otherwise trivial expressions like
length []would cause an ambiguous type error.