我试图使用DerivingVia为一个具有函数依赖关系的多参数类型类削减实例定义的样板。
我有这些类型和类别:
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DerivingVia #-}
newtype Wrapper t = Wrapper t
newtype Wrapper2 t = Wrapper2 t
class MyEq a f | a -> f where
eq :: a -> a -> f Bool
-- Instance for 'Wrapper2'
instance Eq t => MyEq (Wrapper2 t) Wrapper2 where
eq (Wrapper2 t) (Wrapper2 t') = Wrapper2 (t == t')我想使用MyEq (Wrapper Int) Wrapper派生deriving via。
我的第一次尝试是使用:
deriving via Wrapper2 instance MyEq (Wrapper Int) Wrapper正如本文第6.2节https://www.kosmikus.org/DerivingVia/deriving-via-paper.pdf中所讨论的,这是一个MyEq (Wrapper Int) Wrapper2实例,第二个参数被“更改”,但第一个参数仍然是Wrapper Int。
显然,instance MyEq (Wrapper Int) Wrapper2不存在,因为我实现了instance MyEq (Wrapper2 Int) Wrapper2。
我不能通过创建(将Wrapper作为第一个类型参数)来“欺骗”:
-- Instance for 'Wrapper2'
instance Eq t => MyEq (Wrapper t) Wrapper2 where
eq (Wrapper2 t) (Wrapper2 t') = Wrapper2 (t == t')因为在这种情况下,函数依赖关系Wrapper t -> Wrapper2不受尊重。
我可以轻松地通过重写eq :: f a -> f a -> f Bool和消除函数依赖来解决这个问题,但是我希望避免更改这个API。
发布于 2018-10-26 16:13:42
首先,让我们重复一遍,您希望为您派生的实例是这个实例:
instance MyEq (Wrapper Int) Wrapper where
eq (Wrapper t) (Wrapper t') = Wrapper (t == t')我看不出用您想要的方式派生类的方法,因为在您观察自己时,这要求您更改两个类参数,但我们目前只能通过最后一个类进行派生。
一种可能是翻转类参数,这样“重要”类参数(确定另一个类的参数)成为最后一个,然后调整您派生的包装器类型,以包含一些有用的信息,例如:
class MyEq f a | a -> f where
aeq :: a -> a -> f Bool函数aeq保留相同的类型,但是MyEq的类参数被翻转。现在,Wrapper2获得一个额外的参数,让我们在派生时指定f的期望值:
newtype Wrapper2 (f :: Type -> Type) t = Wrapper2 t现在可以定义Wrapper2的实例,而无需显式地指定f。
instance (Eq t, Coercible Bool (f Bool)) => MyEq f (Wrapper2 f t) where
eq (Wrapper2 t) (Wrapper2 t') = coerce (t == t')这里需要Wrapper2中的额外参数来满足函数依赖。
现在,我们可以导出所需的实例,如下所示:
deriving via Wrapper2 Wrapper Int instance MyEq Wrapper (Wrapper Int)这是因为,现在,GHC正在寻找一个instance MyEq Wrapper (Wrapper2 Wrapper Int),这与我们提供的一个匹配。
您可以使用关联类型实现相同的目标:
class MyEq a where
type Result a :: Type -> Type
eq :: a -> a -> Result a BoolWrapper2的定义与额外的参数相同。实例变成
instance (Eq t, Coercible Bool (f Bool)) => MyEq (Wrapper2 f t) where
type Result (Wrapper2 f t) = f
eq (Wrapper2) (Wrapper2 t') = coerce (t == t')
deriving via Wrapper2 Wrapper Int instance MyEq (Wrapper Int)https://stackoverflow.com/questions/53009549
复制相似问题