可以说我有以下内容:
class C a where
f :: a -> Blah
f_impl_for_d :: D a => a -> Blah
f_impl_for_d = _
newtype Alice f a = Alice (f a)
Run Code Online (Sandbox Code Playgroud)
也许一些现有的实例是这样的:
instance C a => C [a] where ...
instance C a => C (Identity a) where ...
instance C a => C (Maybe a) where ...
Run Code Online (Sandbox Code Playgroud)
我想要的是这样的实例:
instance (D a, C a => C (f a)) => C (Alice f a)
Run Code Online (Sandbox Code Playgroud)
就像,只要D a有效,并且有一个C (f a)带有约束的实例C a,那么我应该能够导出C (Alice f a)
例如,如果我有D a,那么我应该有C (Alice [] a),C (Alice Maybe a)等等。
现在我可以这样做:
instance D a => C a where
f = f_impl_for_d
instance C (f a) => C (Alice f a) where
f (Alice x) = f x
Run Code Online (Sandbox Code Playgroud)
但那个最上面的实例是可笑地重叠的。
还有其他办法吗?我解决的唯一方法是创建一个新类型,如下所示:
newtype T x = T x
instance D x => C (T x) where
f (T x) = f_impl_for_d x
instance (Functor f, C (f (T x))) => C (Alice f x) where
f (Alice x) = f (T <$> x)
Run Code Online (Sandbox Code Playgroud)
但这似乎有些令人费解。还有更好的办法吗?我尝试着用量化的约束来解决问题,但并没有走得太远。
C a => C (f a)不完全是你所需要的。你可能会认为它是一个函数C a -> C (f a),但它的含义在技术上比这更精确:C a约束只能使用实例来解决;该函数不能接受任意参数。
您可以定义一个(某种)子类来提供这样的功能:
class (forall a. C a => C (f a)) => C1 f where
f1 :: (a -> Blah) -> (f a -> Blah)
Run Code Online (Sandbox Code Playgroud)
然后必须显式实现,例如Maybe,使用当前的实现C a => C (Maybe a),并且该C实例可以替换为默认的f1 f。
instance C1 Maybe where ...
instance C a => C (Maybe a) where
f = f1 f
Run Code Online (Sandbox Code Playgroud)
那么你就可以拥有
instance (D a, C1 f) => C (Alice f a) where
f (Alice x) = f1 f_impl_for_d x
Run Code Online (Sandbox Code Playgroud)
这类似于base 中的Eq1,Ord1 ,Show1类。最初的目的是对量化的约束进行编码(forall a. Eq a => Eq (f a))(在它们成为事物之前),但从技术上讲,这些类的表达能力稍强一些,因为约束Eq a(标识全局唯一值)被替换为类型(可能有许多值) 。