问题描述
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
其中 a
是 C2
的实例 b
是T1
,只要 T2
不是 C2
的实例,就允许另一个实例。
GHC 将其理解为:forall a
b
是 T1
,从而排除将任何其他类型分配给 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
其中 a
是 C2 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