Haskell 中单个实例的重叠实例错误

Overlapping instances error with single instance in Haskell

我在 Haskell 上有以下代码:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, ExistentialQuantification, ConstraintKinds #-}

import Data.Proxy

class C p a b where
    c :: p b -> a -> Maybe (Ctx b)

class X a

instance X ()

data Ctx ctx = forall a. ctx a => Ctx a

instance C p (Ctx a) a where
    c _ = Just

instance C Proxy a b where
    c _ _ = Nothing

x :: Maybe (Ctx X)
x = c (Proxy :: Proxy X) (Ctx () :: Ctx X)

但是当我尝试编译此代码(或加载到 ghci 中)时出现错误:

    * Overlapping instances for C Proxy (Ctx X) X
        arising from a use of `c'
      Matching instances:
        instance C p (Ctx a) a
          -- Defined at Main.hs:14:10
      There exists a (perhaps superclass) match:
      (The choice depends on the instantiation of `'
       To pick the first instance above, use IncoherentInstances
       when compiling the other instance declarations)
    * In the expression: c (Proxy :: Proxy X) (Ctx () :: Ctx X)
      In an equation for `x': x = c (Proxy :: Proxy X) (Ctx () :: Ctx X)
   |
21 | x = c (Proxy :: Proxy X) (Ctx () :: Ctx X)
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

为什么?

如果我删除最后一个实例声明 instance C Proxy a b,它工作正常。为什么?

我尝试在第一个实例声明中添加 {-# OVERLAPPING #-} pragma 或在第二个实例声明中添加 {-# OVERLAPPABLE #-} 但错误仍然存​​在。为什么?

但是如果我将第二个实例标记为 {-# INCOHERENT #-} 它将编译成功。

实例 C p' (Ctx a') a'C Proxy a' b' 与约束 C Proxy (Ctx a'') b'' 具有相同的特定性 匹配,主要是因为它们都需要在class 头 C p a b,即 p ~ Proxya ~ Ctx a'.

重叠实例仅在编译器可以 select 一个 最具体的 匹配实例时才起作用,这就是为什么此代码需要 IncoherentInstances 以便 select 第一场比赛。如果没有关于您尝试做的事情的更多背景信息,很难就如何更改代码来避免这种情况提出建议。如果您可以确保两个实例产生相同的行为,那么您 不需要 避免它,但它们目前不会。您期望 x 的值是多少?

正如@AntC 在评论中指出的那样,将一个实例优先于另一个实例的一种方法是将实例头的匹配 out 移动,从而使其不那么具体,并改为引入等式约束:

  • instance (p ~ Proxy) => C p a b
  • instance (a ~ Ctx a', b ~ a') => C p a b

此类约束是在匹配实例后引入的。然后可以使用pragmas指定,比如less-specific实例是OVERLAPPABLE,and/or more-specific实例是OVERLAPPING.

如果您打算将您表示的这种关系表示为 函数,那么基于(封闭的)类型族而不是MPTC 的完全通用性。