在Haskell中选择类型类实例时,为什么不考虑上下文?

ron*_*ron 14 haskell typeclass

我明白了

instance (Foo a) => Bar a
instance (Xyy a) => Bar a
Run Code Online (Sandbox Code Playgroud)

GHC不考虑上下文,并且实例报告为重复.

什么是违反直觉的,(我猜)在选择实例后,它仍然需要检查上下文是否匹配,如果不匹配,则丢弃实例.那么为什么不反转顺序,并丢弃具有不匹配上下文的实例,并继续使用剩余的集合.

这会在某种程度上难以解决吗?我看到它如何能够在前期引起更多的约束解析工作,但就像有UndecidableInstances/ IncoherentInstances,不可能有ConsiderInstanceContexts"我知道我在做什么"的时候?

HTN*_*TNW 5

这打破了开放世界的假设。认为:

class B1 a
class B2 a
class T a
Run Code Online (Sandbox Code Playgroud)

如果我们允许约束消除实例的歧义,我们可以写

instance B1 a => T a
instance B2 a => T a
Run Code Online (Sandbox Code Playgroud)

并且可以写

instance B1 Int
Run Code Online (Sandbox Code Playgroud)

现在,如果我有

f :: T a => a
Run Code Online (Sandbox Code Playgroud)

然后f :: Int工作。但是,开放世界的假设说,一旦某些东西起作用,添加更多实例就无法破坏它。我们的新系统不遵守:

instance B2 Int
Run Code Online (Sandbox Code Playgroud)

会造成f :: Int歧义。T应该使用哪个实现?

另一种说法是你打破了连贯性。类型类的一致性意味着只有一种方法可以满足给定的约束。在普通的 Haskell 中,约束c只有一种实现。即使有重叠的实例,一致性通常也适用。这个想法是,instance T ainstance {-# OVERLAPPING #-} T Int不破的连贯性,因为GHC不能被欺骗,在一个地方,后者将用做前者的实例。(你可以用孤儿欺骗它,但你不应该。)至少对我来说,连贯性似乎有些可取。从某种意义上说,类型类的使用是“隐藏的”,强制它明确无误是有意义的。你也可以用IncoherentInstancesand/or打破一致性unsafeCoerce,但是,你知道。

以范畴论的方式,范畴Constraint细的instance从一个Constraint到另一个至多有一个/箭头。我们首先构造两个箭头a : () => B1 Intb : () => B2 Int,然后我们通过添加新箭头 来打破细度x_Int : B1 Int => T Inty_Int : B2 Int => T Int使得x_Int . a和都是不相同的y_Int . b箭头() => T Int。钻石问题,有人吗?


Dan*_*elM 3

这并不能回答您关于为什么会出现这种情况的问题。但请注意,您始终可以定义一个新类型包装器来消除两个实例之间的歧义:

newtype FooWrapper a = FooWrapper a
newtype XyyWrapper a = XyyWrapper a

instance (Foo a) => Bar (FooWrapper a)
instance (Xyy a) => Bar (XyyWrapper a)
Run Code Online (Sandbox Code Playgroud)

这还有一个额外的优点,即通过传递 FooWrapper 或 XyyWrapper,您可以明确控制要使用这两个实例中的哪一个(如果您的 a 恰好满足这两个实例)。

  • 我发现这是一个思想实验,也是一个有趣的问题(我无法回答)。然而,FWIW,我认为编译器做出任意甚至不确定的选择来完全改变代码的行为是一个糟糕的设计。 (4认同)