你是正确的,因为决议,这同forall
关键字做打电话ghci的。不过,这个原因需要一些解释。如果你不关心那么多的解释,那就直接跳到最后。如果你还想要更多,请参阅GHC手册的Syntax Extensions和Other Type System Extensions部分。
Haskell中由类型变量(例如data Heh a = Heh a
)参数化的类型声明本质上是创建一个类型级函数(类型构造函数),它将一个类型作为输入并返回一个新类型。该的Heh
“种”(一种是我们如何划分类型,如类型是如何分类的值)为* -> *
,而Int
是一种*
和Heh Int
(适用于类型Int
的Heh
类型构造函数)是一种*
以及。因此,适用于*
一个* -> *
给出了另一个*
,这是那种正常的类型。
变量,无论是在值级别或类型级别,必须一些结合形式,它定义了程序文本名称的特定变量是在其中结合的范围(区域结合效果)的约束力。在价值层面,我们已经习惯了看到这些粘合剂所有的地方:一些例子在功能定义模式参数绑定,在let
表达式的绑定,并在lambda表达式的绑定(如\x -> x
结合中的名称x
表达的主体)。
混乱始于型级变量;他们似乎根本没有绑定,他们只是有在类型声明的中间。这很大程度上是因为Haskell的类型系统是从Hindley-Milner类型系统开始下降的,而Hindley-Milner类型系统根本不具有类型注释,并且对于可能采用多种类型的表达式采用了不同的思维方式。多态类型的概念变得更加形式化,“polytyped”从辛德米尔纳值成了“多态”和理论的语法类型版本包含的类型级粘合剂和相应的价值层次的粘合剂,将采取型和回报类型变量的表达式主体中的类型被替换的表达式。因为这些绑定器在编译时被完全解析,所以它们被排除在类型和值级别的语法之外。由于在原始系统中,活页夹只能在类型声明中的一个位置出现,因此不存在歧义。
如果你没有按照这一切,不用担心它;要点是,事情的发展是出于历史原因,因为基本概念以及它们在类型化函数式语言中实现的方式不断发展。
反正forall
是类型级变量粘合剂,有点像一个lambda表达式是一个值级变量粘合剂。没有它,假设在每个绑定所有自由类型变量的类型级表达式的开始处有一个隐含的forall
。明确地绑定类型变量使得它们更“清楚”,它们是“普遍量化的”(如果你不熟悉普遍量化的概念,请参阅一阶逻辑的介绍),并且也打开了“存在量化”的可能性“和更高排名的多态类型变量(扩展名为RankNTypes
)。这似乎很奇怪,因为存在量化通常表示通过“存在”的粘合剂,但如果你把一个forall a
内data
类型声明不把a
进入活动范围本身(例如data Foo b = forall a. Foo (a, a -> b)
),你得到相同的效果作为存在量化a
(假设您启用ExistentialQuantification
扩展,当然)。
但是你真正关心的是如何提高你的类型变量量化到整个instance
声明的范围;这是ScopedTypeVariables
扩展的用途。正如我之前所说的,Hindley-Milner类型系统最初并没有使用绑定和自由类型变量的概念(有逻辑解析算法中扮演角色的类型方案(或多类型)和monotypes)甚至类型注释。当首先加入类型注释,它们代表“未知monotypes”和称为刚好在所述特定表达被注释而不是一个范围的结合构造像forall
或λ。因此,如果同一个名称在类型环境中出现不止一次,则它们将被重命名。通过打开ScopedTypeVariables
,在明确绑定相同名称的绑定范围内表达的任何类型变量不再是未知单形变量(即来自错误消息的“刚性类型变量”),而是引用绑定类型变量该名称最近的封闭类型联编程序中的同名名称。
总之,这里的解决您的难题所需要的最终结果是:
{-# LANGUAGE ScopedTypeVariables #-}
data Heh a = Heh a
instance forall a. (Eq a) => Eq (Heh a) where
(Heh a1) == (Heh a2) = (a1 :: a) == a2
的forall
在实例声明的范围遍及实例声明延伸,因为ScopedTypeVariables
是积极的,在提及a
在a1
上的类型注释是指与forall
中的相同的a
绑定。
如果你看的文档,你会发现有结合类型的变量,以及一些其他的语法结构。在这种情况下,forall
实际上不是必需的,因为在class
和instance
声明头部类和实例变量自动绑定中相同的方式的类型变量为forall
确实当扩展是有效的。但是,如果你有需要跨功能的整个身体先限定类型声明一个独立的功能,你需要知道如何使用forall
得到你想要的类型变量的作用域。
是,涉及到'forall'因为这个关键字明确地标志着一个范围 - 但我仍然认为,范围只标明一个函数体内,而不是where子句,但我对'forall'没有专家。 – epsilonhalbe
@ epsilonhalbe对你有正确的答案;您需要'forall'和'ScopedTypeVariables'扩展名的组合。显式'forall'还有其他用途(通过其他扩展),比如创建存在类型,或者只是为了明确。 –
我推荐*总是*使用'ScopedTypeVariables'。 – dfeuer