Haskell 使用数据构造函数在上下文中调用 我阅读了有关数据构造函数的文章,并考虑如下:

Haskell 使用数据构造函数在上下文中调用 我阅读了有关数据构造函数的文章,并考虑如下:,haskell,Haskell,2.1数据构造函数作为一级值 数据构造函数是Haskell中的第一类值,实际上有一个类型。例如, 任一数据类型的左构造函数的类型为: 左::对于所有b a。a->a或b 作为第一类值,可以传递它们 to函数,保存在列表中,是其他代数数据的数据元素 类型等等 上述所有的含义是什么 在所有多态Haskell签名中以某种方式隐式使用。比如说, map :: ∀ a b . (a -> b) -> [a] -> [b] 这被称为通用量化,正如您可能知道的,这意味着对于您选择的任何类型

2.1数据构造函数作为一级值

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

左::对于所有b a。a->a或b

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

上述所有的
含义是什么

在所有多态Haskell签名中以某种方式隐式使用。比如说,

map :: ∀ a b . (a -> b) -> [a] -> [b]
这被称为通用量化,正如您可能知道的,这意味着对于您选择的任何类型
A
B
map
都可以使用,就好像它具有签名
(A->B)->[A]->[B]

在Haskell98中,此通用量化使用
∀ 在签名开始处的一个b c…
(所有发生的类型变量)是唯一可用的多态性,它仍然是最重要的。因此,这是隐式的:当您看到带有小写字母的签名时,编译器知道签名实际上以
覆盖所有这些变量。所以你可以简单地写

map :: (a -> b) -> [a] -> [b]
这还包括数据构造函数(在表达式中,数据构造函数的行为与任何其他函数一样

Left :: a -> Either a b
但正如我所说,这实际上只是

Left :: ∀ a b . a -> Either a b
或者确实

Left :: forall a b . a -> Either a b
在现代Haskell中,有时有必要使用显式量子,而不是隐式量子

  • 要“重用”本地签名中顶级签名中的类型变量,请执行以下操作:

    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
    重新使用已绑定在
    foldl f=…
    模式中的相同的
    f
    ,并且该模式不是多态的(此时类型已经固定),因此不可能在
    go
    中选择独立的
    a'
    b'
    类型变量
    解决方案是启用作用域类型变量,然后显式写入

    {-# 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
    
    在这里,GHC知道我不想要隐式量化(因为我已经明确地编写了一个)。因此
    go
    签名中的
    a
    b
    现在与顶层使用的类型相同

  • 允许参数具有多态性。这称为高阶多态性。问题与上面的问题类似:正如我所说,局部绑定的
    f
    不是多态性的。(通常不能是多态性的–您希望能够将
    foldl
    与特定于一种元素类型的函数一起使用!)
    但在某些应用程序中,您需要的是多态函数as和参数。例如,您可能有一个类型,它可以对精确有理数和快速近似浮点进行运算

    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!
    
    然后可以像这样使用它

    > obBothReprs (+273) 1e+16
    ApproxAndExact 1.0000000000000272e16 (10000000000000273 % 1)
    

虽然该wiki页面解释了所有
的含义,但请注意,问题中使用的所有
并不构成该页面后面解释的存在类型。该页面中与该问题相关的唯一内容是“所有关键字”部分。这样你就不会感到困惑了。从你的个人资料来看,你似乎很熟悉C#?
Left::for all a b.a->a b
就像
或Left(a值)
。所有AB的Haskell
对应于
左侧的C#
——它定义了泛型类型参数的范围。通常这在Haskell中是隐式的,但您可以(有时必须)这样做用“代码”>显式的所有代码>代码>代码> ScopedTypeVariables >代码>或>代码> RankNTypes <代码>扩展。同样,如果使用C++,<>代码> B.< /代码>就像<代码>模板< /代码>。