让我们从一个代码示例开始:
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子句内代码的类型。它使bin与inval :: b相同。bfoob :: forall a b. (b -> b) -> b -> (a -> b) -> Maybe a -> b
一个令人困惑的点:你可能听说当你forall从一个类型中省略时,它实际上仍然隐式存在。(来自诺曼的回答:“通常这些语言从多态类型中省略了 forall”)。这种说法是正确的,但它指的是 的其他用途forall,而不是ScopedTypeVariables用途。
Rank-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-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意味着您的表达式必须适合所有可能a的 s。例如:
ghci> length ([] :: forall a. [a])
0
空列表确实可以作为任何类型的列表。
所以对于存在量化,定义中forall的 sdata意味着包含的值可以是任何合适的类型,而不是它必须是所有合适的类型。