虽然我知道 GHC 中有一个TypeSynonymInstances扩展,但我不知道它有多“危险”,我想知道这个限制是否是任意的,有点像单态限制,或者是否有更深层次的原因。
3 回答
TypeSynonymInstances
非常安全。由于不允许任何可能像部分应用的类型同义词这样的花哨的东西,它与在实例头部中键入类型同义词的右侧具有完全相同的效果,即
type Foo a = ([a], [a])
instance Bar (Foo a)
是相同的
instance Bar ([a], [a])
但是,请注意,这两个实例都需要,FlexibleInstances
因为它们包含嵌套的类型构造函数以及重复的类型变量。一般来说,扩展类型同义词后通常会出现这种情况。
我认为这可能是默认情况下不允许它们的原因。
但是,FlexibleInstances
它也是一个非常安全的扩展。如果您尝试定义重叠实例,例如
instance Xyzzy String
instance Xyzzy [a]
至于为什么FlexibleInstances
默认不可用,我只能猜测是为了简化语言。实例头的标准规则确保实例定义可以重叠的唯一方法是实例头中的类型构造函数是否相同,而FlexibleInstances
检查重叠则稍微困难一些。
据我了解,它有点像单态性限制——摆脱它并没有错,但它会让你面临你可能意想不到的行为。就像单态限制不会伤害任何东西——所有类型仍然有效——这也应该是完全安全的:无论如何,类型同义词都有限制,这会阻止它们做任何比简单的名称缩短更花哨的事情(例如,你永远不能部分应用它们,所以我们不会得到类型级别的 lambda),所以你总是可以用它们定义的右侧替换它们。因此,由于这些定义的右侧可以作为实例头进行检查(或包含进一步的类型同义词以扩展),所以不应该发生任何不安全的事情。
另一方面,正如禁用单态限制会使您面临潜在的奇怪性能特征一样,启用类型同义词实例会使您面临潜在的奇怪类型类错误。因此,让我们启用-XTypeSynonymInstances
并尝试使用类型同义词编写实例:
Prelude> :set -XTypeSynonymInstances
Prelude> instance Num String where (+) = (++)
<interactive>:3:10:
Illegal instance declaration for `Num String'
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Num String'
String
看起来像一个普通的旧类型,所以一开始这可能会令人惊讶;但它确实[Char]
如此,因此根据 Haskell 2010 的严格规则,这个实例是无效的。如果我们通过打开 turn 来放松这些规则-XFlexibleInstances
(顺便说一句,这意味着-XTypeSynonymInstances
),这个例子现在可以工作了:
Prelude> :set -XFlexibleInstances
Prelude> instance Num String where (+) = (++)
... errors about undefined methods ...
Prelude> "a" + "b"
"ab"
但是事情变得很丑:
Prelude> instance Eq String where
Prelude> "a" == "b"
<interactive>:8:5:
Overlapping instances for Eq [Char]
arising from a use of `=='
Matching instances:
instance Eq a => Eq [a] -- Defined in `GHC.Classes'
instance Eq String -- Defined at <interactive>:7:10
In the expression: "a" == "b"
In an equation for `it': it = "a" == "b"
同样,即使String
看起来像一个独特的类型,我们已经有一个 的实例[a]
,所以它与它重叠。(事实上,这可能-XFlexibleInstances
是默认情况下不打开的部分原因。)打开-XOverlappingInstances
是一个比打开更狡猾的想法-XFlexibleInstances
。
它曾经是被允许的,但为了让 Haskell 对初学者不那么充满惊喜,它被禁止了。