What does the `forall` keyword in Haskell/GHC do?

I'm beginning to understand how the forall keyword is used in so-called "existential types" like this:

data ShowBox = forall s. Show s => SB s

This is only a subset, however, of how forall is used and I simply cannot wrap my mind around its use in things like this:

runST :: forall a. (forall s. ST s a) -> a

Or explaining why these are different:

foo :: (forall a. a -> a) -> (Char,Bool)
bar :: forall a. ((a -> a) -> (Char, Bool))

Or the whole RankNTypes stuff...

I tend to prefer clear, jargon-free English rather than the kinds of language which are normal in academic environments. Most of the explanations I attempt to read on this (the ones I can find through search engines) have these problems:

  • They're incomplete. They explain one part of the use of this keyword (like "existential types") which makes me feel happy until I read code that uses it in a completely different way (like runST , foo and bar above).
  • They're densely packed with assumptions that I've read the latest in whatever branch of discrete math, category theory or abstract algebra is popular this week. (If I never read the words "consult the paper whatever for details of implementation" again, it will be too soon.)
  • They're written in ways that frequently turn even simple concepts into tortuously twisted and fractured grammar and semantics.
  • So...

    On to the actual question. Can anybody completely explain the forall keyword in clear, plain English (or, if it exists somewhere, point to such a clear explanation which I've missed) that doesn't assume I'm a mathematician steeped in the jargon?


    Edited to add:

    There were two stand-out answers from the higher-quality ones below, but unfortunately I can only choose one as best. Norman's answer was detailed and useful, explaining things in a way that showed some of the theoretical underpinnings of forall and at the same time showing me some of the practical implications of it. yairchu's answer covered an area nobody else mentioned (scoped type variables) and illustrated all of the concepts with code and a GHCi session. Were it possible to select both as best, I would. Unfortunately I can't and, after looking over both answers closely, I've decided that yairchu's slightly edges out Norman's because of the illustrative code and attached explanation. This is a bit unfair, however, because really I needed both answers to understand this to the point that forall doesn't leave me with a faint sense of dread when I see it in a type signature.


    Let's start with a code example:

    foob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b
    foob postProcess onNothin onJust mval =
        postProcess val
        where
            val :: b
            val = maybe onNothin onJust mval
    

    This code doesn't compile (syntax error) in plain Haskell 98. It requires an extension to support the forall keyword.

    Basically, there are 3 different common uses for the forall keyword (or at least so it seems), and each has its own Haskell extension: ScopedTypeVariables , RankNTypes / Rank2Types , ExistentialQuantification .

    The code above doesn't get a syntax error with either of those enabled, but only type-checks with ScopedTypeVariables enabled.

    Scoped Type Variables:

    Scoped type variables helps one specify types for code inside where clauses. It makes the b in val :: b the same one as the b in foob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b foob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b .

    A confusing point: you may hear that when you omit the forall from a type it is actually still implicitly there. (from Norman's answer: "normally these languages omit the forall from polymorphic types"). This claim is correct, but it refers to the other uses of forall , and not to the ScopedTypeVariables use.

    Rank-N-Types:

    Let's start with that mayb :: b -> (a -> b) -> Maybe a -> b is equivalent to mayb :: forall a b. b -> (a -> b) -> Maybe a -> b mayb :: forall a b. b -> (a -> b) -> Maybe a -> b , except for when ScopedTypeVariables is enabled.

    This means that it works for every a and b .

    Let's say you want to do something like this.

    ghci> let putInList x = [x]
    ghci> liftTup putInList (5, "Blah")
    ([5], ["Blah"])
    

    What's must be the type of this liftTup ? It's liftTup :: (forall x. x -> fx) -> (a, b) -> (fa, fb) . To see why, let's try to code it:

    ghci> let liftTup liftFunc (a, b) = (liftFunc a, liftFunc b)
    ghci> liftTup (x -> [x]) (5, "Hello")
        No instance for (Num [Char])
        ...
    ghci> -- huh?
    ghci> :t liftTup
    liftTup :: (t -> t1) -> (t, t) -> (t1, t1)
    

    "Hmm.. why does GHC infer that the tuple must contain two of the same type? Let's tell it they don't have to be"

    -- test.hs
    liftTup :: (x -> f x) -> (a, b) -> (f a, f b)
    liftTup liftFunc (t, v) = (liftFunc t, liftFunc v)
    
    ghci> :l test.hs
        Couldnt match expected type 'x' against inferred type 'b'
        ...
    

    Hmm. so here ghc doesn't let us apply liftFunc on v because v :: b and liftFunc wants an x . We really want our function to get a function that accepts any possible x !

    {-# LANGUAGE RankNTypes #-}
    liftTup :: (forall x. x -> f x) -> (a, b) -> (f a, f b)
    liftTup liftFunc (t, v) = (liftFunc t, liftFunc v)
    

    So it's not liftTup that works for all x , it's the function that it gets that does.

    Existential Quantification:

    Let's use an example:

    -- test.hs
    {-# LANGUAGE ExistentialQuantification #-}
    data EQList = forall a. EQList [a]
    eqListLen :: EQList -> Int
    eqListLen (EQList x) = length x
    
    ghci> :l test.hs
    ghci> eqListLen $ EQList ["Hello", "World"]
    2
    

    How is that different from Rank-N-Types?

    ghci> :set -XRankNTypes
    ghci> length (["Hello", "World"] :: forall a. [a])
        Couldnt match expected type 'a' against inferred type '[Char]'
        ...
    

    With Rank-N-Types, forall a meant that your expression must fit all possible a s. For example:

    ghci> length ([] :: forall a. [a])
    0
    

    An empty list does work as a list of any type.

    So with Existential-Quantification, forall s in data definitions mean that, the value contained can be of any suitable type, not that it must be of all suitable types.


    Can anybody completely explain the forall keyword in clear, plain English?

    No. (Well, maybe Don Stewart can.)

    Here are the barriers to a simple, clear explanation or forall :

  • It's a quantifier. You have a to have at least a little logic (predicate calculus) to have seen a universal or existential quantifier. If you've never seen predicate calculus or are not comfortable with quantifiers (and I have seen students during PhD qualifying exams who are not comfortable), then for you, there's no easy explanation of forall .

  • It's a type quantifier. If you haven't seen System F and gotten some practice writing polymorphic types, you're going to find forall confusing. Experience with Haskell or ML is not enough, because normally these languages omit the forall from polymorphic types. (In my mind, this is a language-design mistake.)

  • In Haskell in particular, forall is used in ways that I find confusing. (I'm not a type theorist, but my work brings me in contact with a lot of type theory, and I'm quite comfortable with it.) For me, the main source of confusion is that forall is used to encode a type that I myself would prefer to write with exists . It's justified by a tricky bit of type isomorphism involving quantifiers and arrows, and every time I want to understand it, I have to look things up and work out the isomorphism myself.

    If you are not comfortable with the idea of type isomorphism, or if you don't have any practice thinking about type isomorphisms, this use of forall is going to stymie you.

  • While the general concept of forall is always the same (binding to introduce a type variable), the details of different uses can vary significantly. Informal English is not a very good tool for explaining the variations. To really understand what's going on, you need some mathematics. In this case the relevant mathematics can be found in Benjamin Pierce's introductory text Types and Programming Languages, which is a very good book.

  • As for your particular examples,

  • runST should make your head hurt. Higher-rank types (forall to the left of an arrow) are rarely found in the wild. I encourage you to read the paper that introduced runST : "Lazy Functional State Threads". This is a really good paper, and it will give you a much better intuition for the type of runST in particular and for higher-rank types in general. The explanation take several pages, it's very well done, and I'm not going to try to condense it here.

  • Consider

    foo :: (forall a. a -> a) -> (Char,Bool)
    bar :: forall a. ((a -> a) -> (Char, Bool))
    

    If I call bar , I can simply pick any type a that I like, and I can pass it a function from type a to type a . For example, I can pass the function (+1) or the function reverse . You can think of the forall as saying "I get to pick the type now". (The technical word for picking the type is instantiating.)

    The restrictions on calling foo are much more stringent: the argument to foo must be a polymorphic function. With that type, the only functions I can pass to foo are id or a function that always diverges or errors, like undefined . The reason is that with foo , the forall is to the left of the arrow, so as the caller of foo I don't get to pick what a is—rather it's the implementation of foo that gets to pick what a is. Because forall is to the left of the arrow, rather than above the arrow as in bar , the instantiation takes place in the body of the function rather than at the call site.

  • Summary: A complete explanation of the forall keyword requires math and can be understood only by someone who has studied the math. Even partial explanations are hard to understand without math. But maybe my partial, non-math explanations help a little. Go read Launchbury and Peyton Jones on runST !


    Addendum: Jargon "above", "below", "to the left of". These have nothing to do with the textual ways types are written and everything to do with abstract-syntax trees. In the abstract syntax, a forall takes the name of a type variable, and then there is a full type "below" the forall. An arrow takes two types (argument and result type) and forms a new type (the function type). The argument type is "to the left of" the arrow; it is the arrow's left child in the abstract-syntax tree.

    Examples:

  • In forall a . [a] -> [a] forall a . [a] -> [a] , the forall is above the arrow; what's to the left of the arrow is [a] .

  • In

    forall n f e x . (forall e x . n e x -> f -> Fact x f) 
                  -> Block n e x -> f -> Fact x f
    

    the type in parentheses would be called "a forall to the left of an arrow". (I'm using types like this in an optimizer I'm working on.)


  • My original answer:

    Can anybody completely explain the forall keyword in clear, plain English

    As Norman indicates, it is very hard to give a clear, plain English explanation of a technical term from type theory. We're all trying though.

    There is only really one thing to remember about 'forall': it binds types to some scope . Once you understand that, everything is fairly easy. It is the equivalent of 'lambda' (or a form of 'let') on the type level -- Norman Ramsey uses the notion of "left"/"above" to convey this same concept of scope in his excellent answer.

    Most uses of 'forall' are very simple, and you can find them introduced in the GHC Users Manual, S7.8., particularly the excellent S7.8.5 on nested forms of 'forall'.

    In Haskell, we usually leave off the binder for types, when the type is universally quanitified, like so:

    length :: forall a. [a] -> Int
    

    is equivalent to:

    length :: [a] -> Int
    

    That's it.

    Since you can bind type variables now to some scope, you can have scopes other than the top level ("universally quantified"), like your first example, where the type variable is only visible within the data structure. This allows for hidden types ("existential types"). Or we can have arbitrary nesting of bindings ("rank N types").

    To deeply understand type systems, you will need to learn some jargon. That's the nature of computer science. However, simple uses, like above, should be able to be grasped intuitively, via analogy with 'let' on the value level. A great introduction is Launchbury and Peyton Jones.

    链接地址: http://www.djcxy.com/p/5670.html

    上一篇: Haskell中的孤立实例

    下一篇: Haskell / GHC中的`forall`关键字是做什么的?