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

前端 未结 8 1726
[愿得一人]
[愿得一人] 2020-11-28 17:01

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

data ShowBox = forall s. Show s =>          


        
8条回答
  •  鱼传尺愫
    2020-11-28 17:28

    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?

    I am going to try and explain just the meaning and maybe the application of forall in the context of Haskell and its type systems.

    But before you understand that I would like to direct you to a very accessible and nice talk by Runar Bjarnason titled "Constraints Liberate, Liberties Constrain". The talk is full of examples from real world use cases as well as examples in Scala to support this statement, although it doesn't mention forall. I will try to explain the forall perspective below.

                    CONSTRAINTS LIBERATE, LIBERTIES CONSTRAIN
    

    Its very important to digest and believe this statement to proceed with the following explanation, so I urge you to watch the talk(at least parts of it).

    Now a very common example, showing the expressiveness of the Haskell type system is this type signature:

    foo :: a -> a

    It is said that given this type signature, there is only one function that can satisfy this type and that is the identity function or what is more popularly known id.

    In the beginning stages of me learning Haskell, I always wondered the below functions:

    foo 5 = 6
    
    foo True = False
    

    they both satisfy the above type signature, then why do Haskell folks claim that it is id alone which satisfies the type signature?

    That is because there is an implicit forall hidden in the type signature. The actual type is:

    id :: forall a. a -> a
    

    So, now let us come back to the statement: Constraints liberate, liberties constrain

    Translating that to the type system, this statement becomes:

    A constraint at the type level, becomes a liberty at the term level

    and

    A liberty at the type level, becomes a constraint at the term level


    Let us try to prove the first statement:

    A constraint at the type level..

    So putting a constraint on our type signature

    foo :: (Num a) => a -> a
    

    becomes a liberty at the term level gives us the liberty or flexibility to write all of these

    foo 5 = 6
    foo 4 = 2
    foo 7 = 9
    ...
    

    Same can be observed by constraining a with any other typeclass etc

    So now what this type signature: foo :: (Num a) => a -> a translates to is:

    ∃a , st a -> a, ∀a ∈ Num
    

    This is known as existential quantification, which translates to there exists some instances of a for which a function when fed something of type a returns something of the same type, and those instances all belong to the set of Numbers.

    Hence we can see adding a constraint(that a should belong to the set of Numbers), liberates the term level to have multiple possible implementations.


    Now coming to the second statement and the one which actually carries the explanation of forall:

    A liberty at the type level, becomes a constraint at the term level

    So now let us liberate the the function at the type level:

    foo :: forall a. a -> a
    

    Now this translates to:

    ∀a , a -> a
    

    which means that the implementation of this type signature should be such that it is a -> a for all circumstances.

    So now this starts constraining us at the term level. We can no longer write

    foo 5 = 7
    

    because this implementation would not satisfy if we put a as a Bool. a can be a Char or a [Char] or a custom datatype. Under all circumstances it should return something of the similar type. This liberty at the type level is what is known as Universal Quantification and the only function which can satisfy this is

    foo a = a
    

    which is commonly known as the identity function


    Hence forall is a liberty at the type level, whose actual purpose is to constrain the term level to a particular implementation.

提交回复
热议问题