限制和基金决议

问题描述

我有一对类,C1和C2,在C1上有一个fundep,如下:

class C1 a b | a -> b
class C2 a

我声明一个实例:

instance (C2 a) => C1 a T1

当我声明另一个实例时,出现错误

instance  C1 T2 T3
functional dependencies conflict between instance declarations:
  instance C2 a => C1 a T1
  instance C1 T2 T3

基本上,我希望 T1 实例的意思是:forall a 其中 aC2 的实例 bT1,只要 T2 不是 C2 的实例,就允许另一个实例。

GHC 将其理解为:forall a bT1,从而排除将任何其他类型分配给 b。 >

我知道我可以通过将 a 包装在 newtype 中来解决这个问题,但这对我的应用程序来说很尴尬。

谁能解释一下如何获得我想要的行为(如果确实可能的话),或者解释一下为什么不可以?

解决方法

您的实例在 FunDep 的“参数”位置重叠。这应该有效(未经测试):

{-# LANGUAGE GADTs,UndecidableInstances #-}

instance {-# OVERLAPPABLE #-} (C2 a,b ~ T1) => C1 a b

instance C1 T2 T3

这是利用 GHC 的“虚假”FunDep 一致性检查。 ('bogus' 是 SPJ 编译器代码中的注释。)它不应该工作;这是非常“欺骗”;总有一天有人会修复它,而上述代码将停止工作。

OTOH 自 2008 年以来一直是威胁;它仍然没有得到解决;似乎没有人(除了我)有更好的建议。

以上没有实现的是你的

我打算用 T1 实例表示:forall a 其中 aC2 b 的实例是 T1,只要 T2 允许另一个实例{1}} 不是 C2 的实例。

但是您知道 T2 是否是 C2 的实例 -- 它是特定类型(?)

如果您的意思是针对任何类型 t2,我认为您需要重新表述这个问题。如果有更高级的重叠和 FunDeps,这可能是可行的——而且还有更多的虚假。

考虑一个辅助类:

{-# LANGUAGE DataKinds #-}
class IsC2 a (t :: Bool) | a -> t

instance IsC2 T2 True

instance (t ~ False) => IsC2 a  t

对于作为 instance IsC2 t2 True 实例的每个特定类型 t2,您都需要一个 C2

instance (IsC2 a t,C1C2 t a T1 T3 b) => C1 a b

class C1C2 t a bt bf b  | t bt bf -> b
instance (C2 a) => C1C2 True  a bt bf bt   -- constraint as belt and braces
instance C1C2 False a bt bf bf