Originally I assumed the scope of a nested universal quantifier on the LHS of a function type can be determined purely syntactical, i.e. everything inside the parenthesis of (forall a. a -> b) -> Bool
is within the same scope. However, this assumption is wrong:
{-# LANGUAGE RankNTypes #-}
fun :: (forall a. [a] -> b) -> Bool
fun _ = undefined
arg :: c -> c
arg _ = undefined
r = fun arg -- type variable a would escape its scope
This makes sense, because in fun
b
must be chosen before a
and thus be fixed or independent of a
. During unification the argument type c -> c
would force b
's instantiation with [a0]
though.
So maybe scoping at the type level rather resembles that of functions at the term level, where the result value is clearly not part of the function's scope. In other words if b
weren't the codomain of the function type, the type checker would pass. Unfortunately, I couldn't come up with an annotation that supports my reasoning.
A more restrictive way would be to disallow the instantiation of rigid type variables with any flexible one of the same annotation during unification. Both ways seem legitimate to me, but how does this actually work in Haskell?