给定一个类似的函数negate
,它具有类型签名:
negate :: Num a => a -> a
我将其描述为(如果您认为我错了,请纠正我)a
的上下文中的类型。Num
但我不完全确定如何描述类似的东西last
,它具有类型签名:
last :: [a] -> a
我的猜测是说它不是特定于类型的,它需要一个列表并产生与列表相同类型的单个值。这是正确的思考方式吗?
给定一个类似的函数negate
,它具有类型签名:
negate :: Num a => a -> a
我将其描述为(如果您认为我错了,请纠正我)a
的上下文中的类型。Num
但我不完全确定如何描述类似的东西last
,它具有类型签名:
last :: [a] -> a
我的猜测是说它不是特定于类型的,它需要一个列表并产生与列表相同类型的单个值。这是正确的思考方式吗?
首先,a
不是上下文中的Num
类型,而是具有Num
实例的类型。
Num a => a -> a
是受约束的多态类型,[a] -> a
而是不受约束的多态类型,或者简称为多态类型。在不受约束的情况下,a
可以是任何类型;在受约束的情况下,它必须是服从给定约束的类型。
在negate
您需要对传递的元素进行操作时,在您的情况下,您可以对其应用一些运算符,例如(-)
: negate a = -a
。
您不能negate
为任何类型定义,因为您需要能够调用(-)
它。您需要保证给定的参数将是支持此操作的某种类型。
Num
是一个类型类,它为您提供了这一点 - 支持的编译时保证(-)
,以及其他功能,如+
,等*
。您可以在文档中阅读更多相关信息
相反,last :: [a] -> a
不需要(不需要)对实际a
值做任何事情。它只接受它们并返回最后一个。虽然negate
对a
值last
进行操作,但这里对list进行操作,但不对它的值做任何事情,它只是传递它们。因此,它不需要任何关于它们的知识,因此类型不受限制。
last :: [a] -> a
是System 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
实例的类型(两者都有Int
,Double
但不是 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’