=> 在类型签名中是什么意思?

What does => mean in a type signature?

我刚开始学你一个Haskell,我在没有解释的例子中看到了这个:

tell :: (Show a) => [a] -> String

这是什么意思,特别是=>?我知道如果我替换 -> 或删除它不会起作用,但我真的不明白为什么。

这是对类型的约束,意味着 a 应该是 class Show.

的实例

参见示例 here

=>Chapter 3 - Types and Typeclasses中首次介绍和解释:

What's the type signature of the == function?

ghci> :t (==)  
(==) :: (Eq a) => a -> a -> Bool  

Note: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it's considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.

Interesting. We see a new thing here, the => symbol. Everything before the => symbol is called a class constraint. We can read the previous type declaration like this: the equality function takes any two values that are of the same type and returns a Bool. The type of those two values must be a member of the Eq class (this was the class constraint).

这是另一种看待它的方式。一些函数的参数是不可见的,其他的是可见的。类型 input -> output 告诉我们可见的 input 应该作为参数。类型 (Constraint) => output 告诉我们需要一些不可见的信息。它们不可互换,因为必须写入可见参数,而不得写入不可见参数。不可见的参数是编译器自己弄清楚的(好吧,他对我来说听起来像是他自己),他坚持要弄清楚它们:他拒绝只是被告知它们是什么!

秘密地,这个tell例子的完整类型是

tell :: forall (a :: *). (Show a) => [a] -> String

我所做的就是弄清楚这个a变量是从哪里进来的,它是个什么样的东西。您也可以将此解读为 "deal":tell 提供适用于满足需求 (Show a).

的所有类型 a

为了使 tell 的用法有意义,它需要三件事。其中两个是不可见的,一个是可见的。也就是说,当您使用 tell 时,您使可见参数显式化,并且编译器会尝试填充不可见部分。让我们更慢地处理该类型。

tell :: forall (a :: *).   -- the type of elements to tell          (invisible)
        (Show a) =>        -- how to make a String from one element (invisible)
        [a] ->             -- the list of elements to be told       (visible)
        String             -- the String made by showing all the elements

因此,当您使用 tell 时,例如

tell [True, False]

您只提供可见参数:要说明的内容列表 [True, False],编译器计算出不可见参数。他知道 TrueFalse 都是 Bool 类型的值,所以这意味着

[True, False] :: [Bool]

这就是编译器如何计算出 tell 类型中的 a 必须是 Bool,使得 [a] = [Bool]

(顺便说一句,关于 [True, False] :: [Bool]。:: 左边,方括号,[..],生成列表值。:: 右边,方括号,[..],生成一种列表。它们对你来说可能只是灰色背景上的黑色,但我的大脑将价值生成括号标记为红色,类型生成括号标记为蓝色。它们完全不同。我希望我可以在这个站点上对代码进行颜色标记。我跑题了。)

所以,现在,另一个不可见的参数必须满足这个 (Show a) 的东西,我们现在知道具体是 (Show Bool) 因为我们发现 aBool .我们将这部分类型称为 "constraint",但实际上它不仅要求事实为真,而且要求存在一些有用的东西。这里要求的东西是有一个功能

show :: Bool -> String

就是在对tell [True, False].

求值的过程中,将单个元素TrueFalse转为String的函数

标识符Show类型class的名字,show是那个方法输入 class。类型 class 指定必须为每个 instance 实现的操作接口。该函数的不可见参数是一个记录(或 "dictionary"),它包装了所讨论类型的那些操作的实现(这里是 show 的实现)。没有这些信息,编译后的代码将无法完成它的工作,但我们不必编写这些信息,因为编译器可以(至少在这种情况下)搜索它知道的实例并填写正确的一份工作。

所以,我们不只是有不可见的类型参数(在编译时推断,然后在 运行 时间之前被擦除),由小写类型变量发出信号,或者更明确地由 forall 废话 .。我们还有类型 class 操作的不可见实现(在编译时查找,以提供重要的 运行 时间信息)。因此,在推断和擦除类型之间发生了一些非常重要的事情:虽然编译器仍然知道类型,但它使用它们来确定在 运行 时间需要哪些不可见的实现,这样我们就可以不用自己写了。

缩小,=> 在一个类型中记录了我们期望编译器将利用类型信息来指导我们不必费心编写的 运行 时间代码的生成。这是一个不错的小胜利,就在那里。

类型系统黑客的别有用心。 不可见-可见区别与可擦除-有用区别在不同位置的消息是一些人尚未收到的消息.这就是 classic Hindley-Milner 的立场,但事实是这些区别是正交的,人们越早学会享受这一点越好。