What is the purpose of the foralls in this code?
class Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
-- Explicit for-alls so that we know what order to
-- give type arguments when desugaring
(some code omitted). This is from the code for Monads.
My background: I don’t really understand forall or when Haskell has them implicitly.
Also, and it may not be significant, but GHCi allows me to omit the forall when giving >> a type:
Prelude> :t (>>) :: Monad m => m a -> m b -> m b
(>>) :: Monad m => m a -> m b -> m b
:: (Monad m) => m a -> m b -> m b
(no error).
Okay, consider the type of
id,a -> a. What doesamean, and where does it come from? When you define a value, you can’t just use arbitrary variables that aren’t defined anywhere. You need a top-level definition, or a function argument, or awhereclause, &c. In general, if you use a variable, it must be bound somewhere.The same is true of type variables, and
forallis one such way to bind a type variable. Anywhere you see a type variable that isn’t explicitly bound (for example,class Foo a where ...bindsainside the class definition), it’s implicitly bound by aforall.So, the type of
idis implicitlyforall a. a -> a. What does this mean? Pretty much what it says. We can get a typea -> afor all possible typesa, or from another perspective, if you pick any specific type you can get a type representing “functions from your chosen type to itself”. The latter phrasing should sound a bit like defining a function, and as such you can think offorallas being similar to a lambda abstraction for types.GHC uses various intermediate representations during compilation, and one of the transformations it applies is making the similarity to functions more direct: implicit
foralls are made explicit, and anywhere a polymorphic value is used for a specific type, it is first applied to a type argument.We can even write both
foralls and lambdas as one expression. I’ll abuse notation for a moment and replaceforall a.with/\a =>for visual consistency. In this style, we can defineid = /\a => \(x::a) -> (x::a)or something similar. So, an expression likeid Truein your code would end up translated to something likeid Bool Trueinstead; justid Truewould no longer even make sense.Just as you can reorder function arguments, you can likewise reorder the type arguments, subject only to the (rather obvious) restriction that type arguments must come before any value arguments of that type. Since implicit
foralls are always the outermost layer, GHC could potentially choose any order it wanted when making them explicit. Under normal circumstances, this obviously doesn’t matter.I’m not sure exactly what’s going on in this case, but based on the comments I would guess that the conversion to using explicit type arguments and the desugaring of
donotation are, in some sense, not aware of each other, and therefore the order of type arguments is specified explicitly to ensure consistency. After all, if something is blindly applying two type arguments to an expression, it matters a great deal whether that expression’s type isforall a b. m a -> m b -> m borforall b a. m a -> m b -> m b!