描述非特定类型的 Haskell 函数的类型签名的方法是什么?

What is the way to describe the type signature of Haskell functions that are not type-specific?

给定像 negate 这样的函数,它具有类型签名:

negate :: Num a => a -> a

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

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

last :: [a] -> a

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

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

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

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

您不能为 any 类型定义 negate,因为您需要能够对其调用 (-)。你需要一些保证给定的参数将是支持此操作的某种类型。

Num 是一种 class 类型,它为您提供了这样的 - 支持 (-) 的编译时保证,以及其他功能,如 +*,也是。您可以在 docs

中阅读更多相关信息

相比之下,last :: [a] -> a 而不是 (需要)对实际的 a 值做任何事情。它只接受它们和 returns 最后一个。虽然 negatea 值进行操作,但此处 lastlist 进行操作,但不对其值进行任何操作,它仅传递它们大约。因此,它不需要任何关于它们的知识,因此类型不受约束。

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 可以,但不可以,例如 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’