我在HaskellWiki上阅读了有关数据构造函数的内容,并考虑以下内容:
2.1数据构造函数作为第一类值
数据构造函数是Haskell中的第一类值,实际上有一个类型。例如, Either数据类型的Left构造函数的类型是:
Left :: forall b a. a -> Either a b
作为头等值,可以传递它们 保存在列表中的函数是其他代数数据的数据元素 类型等等。
上面的forall
是什么意思?
答案 0 :(得分:6)
所有多态Haskell签名中隐式使用∀
quantor, aka forall
。{{3}}。例如,
map :: ∀ a b . (a -> b) -> [a] -> [b]
这称为通用量化,您可能知道它意味着您选择的任何类型A
和B
的,{ {1}}可以像使用签名map
一样使用。
在Haskell98中,在签名开始时使用(A -> B) -> [A] -> [B]
(发生的所有类型变量)的这种通用量化是 only 类型的多态可用,并且它是可用的仍然是最重要的。因此这是隐含的:当您看到带有小写字母的签名时,编译器知道签名确实以∀ a b c ...
开头,而不是所有这些变量。所以你可以简单地写
∀
还包括数据构造函数(表达式中的行为与任何其他函数一样。您通常会写
map :: (a -> b) -> [a] -> [b]
但正如我所说,这只是
的简写Left :: a -> Either a b
或确实
Left :: ∀ a b . a -> Either a b
在现代的Haskell中,有时需要使用显式量子而不是隐式量子。即,
从本地签名中的顶级签名“重用”类型变量。例如,以下内容不起作用:
Left :: forall a b . a -> Either a b
问题是由于其中的类型变量,foldl :: (b->a->b) -> b -> [a] -> b
foldl f = go
where go :: b -> [a] -> b
go acc [] = acc
go acc (x:xs) = go (f acc x) xs
的本地签名意味着新的通用量子,即这实际上意味着
go
但是,foldl :: ∀ a b . (b->a->b) -> b -> [a] -> b
foldl f = go
where go :: ∀ a' b' . b' -> [a'] -> b'
go acc [] = acc
go acc (x:xs) = go (f acc x) xs -- error: could not match a with a'
重新使用已在go
模式中绑定的f
,并且不是多态(类型有已经修复了那个点),因此无法在foldl f = ...
中选择独立的a'
和b'
类型变量。
解决方案是启用范围类型变量然后显式写
go
在这里,GHC知道我不想要隐式量子(因为我已经明确地写了一个)。因此,{-# LANGUAGE ScopedTypeVariables #-}
foldl :: ∀ a b . (b->a->b) -> b -> [a] -> b
foldl f = go
where go :: b -> [a] -> b
go acc [] = acc
go acc (x:xs) = go (f acc x) xs
签名中的a
和b
现在与顶层使用的类型相同。
允许参数具有多态性。这被称为更高级别的多态性。问题类似于上面的问题:正如我所说,本地绑定的go
不是多态。 (它通常不会 - 您希望能够使用f
具有特定于某个特定元素类型的函数!)
但在某些应用程序中,多态函数as和参数是你想要的。例如,您可能有一个类型,它在精确有理数和快速近似浮点中都是算术运算。
foldl
现在您想对这些号码执行操作,但不想复制您的代码。所以你使用的是
data ApproxAndExact = ApproxAndExact Double Rational
然后可以像
一样使用onBothReprs :: (∀ n . Fractional n => n -> n)
-> ApproxAndExact -> ApproxAndExact
onBothReprs f (ApproxAndExact approx exact)
= ApproxAndExact (f approx) (f exact) -- note that both invocations
-- of f have different types!