使用Data构造函数的Context中的forall

时间:2017-08-02 07:08:25

标签: haskell

我在HaskellWiki上阅读了有关数据构造函数的内容,并考虑以下内容:

  

2.1数据构造函数作为第一类值

     

数据构造函数是Haskell中的第一类值,实际上有一个类型。例如,   Either数据类型的Left构造函数的类型是:

     

Left :: forall b a. a -> Either a b

     

作为头等值,可以传递它们   保存在列表中的函数是其他代数数据的数据元素   类型等等。

上面的forall是什么意思?

1 个答案:

答案 0 :(得分:6)

所有多态Haskell签名中隐式使用 quantor, aka forall。{{3}}。例如,

map :: ∀ a b . (a -> b) -> [a] -> [b]

这称为通用量化,您可能知道它意味着您选择的任何类型AB,{ {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 签名中的ab现在与顶层使用的类型相同。

  • 允许参数具有多态性。这被称为更高级别的多态性。问题类似于上面的问题:正如我所说,本地绑定的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!