I\'m beginning to understand how the forall keyword is used in so-called \"existential types\" like this:
data ShowBox = forall s. Show s =>
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:
and
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.