让我们从一个代码示例开始:
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关键字。
基本上,forall关键字有3种不同的常用用法(至少看起来是这样),每一种都有自己的Haskell扩展:ScopedTypeVariables, RankNTypes/Rank2Types, ExistentialQuantification。
上面的代码在启用这两种情况下都不会出现语法错误,而只在启用ScopedTypeVariables时进行类型检查。
作用域类型变量:
限定作用域的类型变量有助于在where子句中为代码指定类型。它使得val:: b中的b与foob:: forall中的b相同。(b -> b) -> b -> (a -> b) ->也许是a -> b。
令人困惑的一点是:当你从一个类型中省略forall时,它实际上仍然隐式存在。(来自Norman的回答:“通常这些语言会从多态类型中省略forall”)。这个声明是正确的,但它指的是forall的其他用法,而不是ScopedTypeVariables的用法。
Rank-N-Types:
让我们从mayb:: b -> (a -> b) ->也许a -> b等价于mayb:: forall ab b -> (a -> b) ->也许a -> b,除非ScopedTypeVariables被启用。
这意味着它对所有a和b都成立。
假设你想做这样的事情。
ghci> let putInList x = [x]
ghci> liftTup putInList (5, "Blah")
([5], ["Blah"])
这个电梯一定是什么类型的?它是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不让我们在v上应用liftFunc,因为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-Types有什么不同?
ghci> :set -XRankNTypes
ghci> length (["Hello", "World"] :: forall a. [a])
Couldnt match expected type 'a' against inferred type '[Char]'
...
对于Rank-N-Types, forall a意味着你的表达式必须适合所有可能的。例如:
ghci> length ([] :: forall a. [a])
0
空列表可以作为任何类型的列表。
因此,对于存在量化,数据定义中的foralls意味着,所包含的值可以是任何合适的类型,而不是必须是所有合适的类型。