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 ~ Proxy
或 a ~ 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 的完全通用性。
我在 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 ~ Proxy
或 a ~ 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 的完全通用性。