4

给定一个类似的函数negate,它具有类型签名:

negate :: Num a => a -> a

我将其描述为(如果您认为我错了,请纠正我)a的上下文中的类型。Num

但我不完全确定如何描述类似的东西last,它具有类型签名:

last :: [a] -> a

我的猜测是说它不是特定于类型的,它需要一个列表并产生与列表相同类型的单个值。这是正确的思考方式吗?

4

3 回答 3

5

首先,a不是上下文中Num类型,而是具有Num实例的类型。

Num a => a -> a是受约束的多态类型,[a] -> a而是不受约束的多态类型,或者简称为多态类型。在不受约束的情况下,a可以是任何类型;在受约束的情况下,它必须是服从给定约束的类型。

于 2021-02-08T15:24:36.283 回答
2

negate您需要对传递的元素进行操作时,在您的情况下,您可以对其应用一些运算符,例如(-): negate a = -a

您不能negate任何类型定义,因为您需要能够调用(-)它。您需要保证给定的参数将是支持此操作的某种类型。

Num是一个类型类,它为您提供了这一点 - 支持的编译时保证(-),以及其他功能,如+,等*。您可以在文档中阅读更多相关信息

相反,last :: [a] -> a不需要(不需要)对实际a值做任何事情。它只接受它们并返回最后一个。虽然negatealast进行操作,但这里对list进行操作,但不对它的值做任何事情,它只是传递它们。因此,它不需要任何关于它们的知识,因此类型不受限制。

于 2021-02-08T15:27:14.243 回答
0

last :: [a] -> aSystem F类型的 Haskell98 语法

last :: ∀ a. [a] -> a

∀ a可以理解为一种类型级别的 lambda 绑定,即在实际值级别列表参数之前,函数接受一个类型级别参数,告诉列表中包含的元素的类型。这种普遍的量化使函数参数化多态

通常,类型变量由类型检查器自动插入。在较新的 GHC Haskell 中,您还可以显式应用它们:

Prelude> :set -XTypeApplications 
Prelude> :t last @Int
last @Int :: [Int] -> Int
Prelude> last @Double [5,6,7]
7.0

negate也是参数多态的,但与last它不同的是,它不能真正“适用于所有”类型,而仅适用于那些具有Num实例的类型(两者都有IntDouble但不是 eg Char)。换句话说,它不仅接受指定类型的额外参数,还接受它确实有实例的证明。Num这也将由编译器自动插入。

negate :: ∀ a. Num a => a -> a
Prelude> :t negate @Int
negate @Int :: Int -> Int
Prelude> :t negate @Char

<interactive>:1:1: error:
    No instance for (Num Char) arising from a use of ‘negate’
于 2021-02-09T13:34:44.357 回答