让我们从一个代码示例开始:
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
该代码在普通的Haskell 98中不会编译(语法错误)。它需要扩展来支持forall关键字。
基本上,关键字有3 种不同的常用用法forall(或至少看起来如此),每种都有自己的Haskell扩展名:ScopedTypeVariables,RankNTypes / Rank2Types,ExistentialQuantification。
上面的代码都不会在启用其中任何一个时发生语法错误,而只会在ScopedTypeVariables启用时进行类型检查。
作用域类型变量:
作用域类型变量有助于为where子句中的代码指定类型。这使得b在val :: b同样的一个作为b在foob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b。
令人困惑的一点:您可能会听到,当您forall从类型中省略时,它实际上仍然隐式存在。(摘自Norman的回答:“通常,这些语言会忽略多态类型的所有内容”)。此声明是正确的,但它涉及的其他用途forall,而不是该ScopedTypeVariables用途。
排名N型:
让我们先从这mayb :: b -> (a -> b) -> Maybe a -> b相当于mayb :: forall a b. b -> (a -> b) -> Maybe a -> b,除外时ScopedTypeVariables启用。
这意味着,它适用于所有的a和b。
假设您想做这样的事情。
ghci> let putInList x = [x]
ghci> liftTup putInList (5, "Blah")
([5], ["Blah"])
此类型必须是什么liftTup?是liftTup :: (forall x. x -> f x) -> (a, b) -> (f a, f b)。要了解原因,请尝试对其进行编码:
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)
“嗯。为什么GHC推断该元组必须包含两个相同类型的元组?让我们知道它们不一定必须是两个。”
-- 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'
    ...
嗯 所以这里GHC没有让我们应用liftFunc上v,因为v :: b和liftFunc希望的x。我们真的希望我们的函数能够接受任何可能的函数x!
{-# LANGUAGE RankNTypes #-}
liftTup :: (forall x. x -> f x) -> (a, b) -> (f a, f b)
liftTup liftFunc (t, v) = (liftFunc t, liftFunc v)
因此,这并非liftTup对所有人都有效x,而是它所获得的功能。
存在量化:
我们来看一个例子:
-- 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
与Rank-N-Type有什么不同?
ghci> :set -XRankNTypes
ghci> length (["Hello", "World"] :: forall a. [a])
    Couldnt match expected type 'a' against inferred type '[Char]'
    ...
使用Rank-N-Types forall a意味着您的表达式必须适合所有可能的as。例如:
ghci> length ([] :: forall a. [a])
0
空列表确实可以用作任何类型的列表。
因此,对于“存在量化”,定义中的foralls data表示所包含的值可以是任何合适的类型,而不是必须是所有合适的类型。