【发布时间】:2021-06-16 16:00:45
【问题描述】:
在 Simon Peyton Jones、Mark Jones 和 Erik Meijer 的论文 "Type classes: exploring the design space" 中,他们非正式地定义了一致性如下:
程序的每个不同的有效类型派生都会导致生成的程序具有相同的动态语义。
首先,程序没有类型;表达式、变量和函数都有类型。所以我想我会把它解释为每个程序片段(表达式、变量或函数)必须有一个唯一的类型推导。
那我想知道 Haskell(比如 Haskell2010)是否真的连贯?例如。表达式\x -> x 可以赋予类型a -> a,也可以赋予Int -> Int。这是否意味着连贯性被破坏了?我能想到两个反驳:
-
Int -> Int不是一个有效的类型推导,术语\x -> x得到推断类型a -> a,它比Int -> Int严格更通用。 -
两种情况下的动态语义是相同的,只是
Int -> Int类型不太通用,在某些情况下会被静态拒绝。
以下哪些是正确的?还有其他反驳吗?
现在让我们考虑类型类,因为在这种情况下经常使用连贯性。
GHC 实现的 Haskell 有多种方式可能会破坏一致性。显然IncoherentInstances 扩展和相关的INCOHERENT pragma 似乎相关。孤儿实例也会浮现在脑海中。
但是如果上面的第 1 点是正确的,那么我会说即使这些也不会破坏连贯性,因为我们可以说 GHC 选择的实例是应该选择的一个真实实例(以及所有其他类型推导无效),就像 GHC 推断的类型是必须选择的真实类型一样。所以第 1 点可能不正确。
还有更多看似无害的扩展,例如通过OverlappingInstances 扩展或Overlapping、Overlaps 和Overlappable pragma 重叠实例,但即使MultiParamTypeClasses 和FlexibleInstances 的组合也可以产生重叠的实例。例如
class A a b where
aorb :: Either a b
instance A () b where
aorb = Left ()
instance A a () where
aorb = Right ()
x :: Either () ()
x = aorb
FlexibleInstances 和 MultiParamTypeClasses 扩展名包含在 GHC2021 中,所以我当然希望它们不会破坏连贯性。但我不认为上面的第 1 点是正确的,第 2 点在这里似乎并不适用,因为动态语义确实不同。
我还想提一下默认系统。考虑:
main = print (10 ^ 100)
默认情况下,GHC(可能还有 Haskell2010?)将默认为 Integer 和 10 使用 100。所以结果打印出一个有一百个零的一。但是如果我们现在添加一个自定义的默认规则:
default (Int)
main = print (10 ^ 100)
那么10 和100 都默认为Int 类型,并且由于包装它只打印一个零。所以表达式10 ^ 100在不同的上下文中具有不同的动态语义。这是不连贯的吗?
所以我的问题是:是否有更正式或更详细的连贯性定义可以解决上述问题?
【问题讨论】:
标签: haskell ghc type-systems