Haskell DerivingVia在多参数类型类上的应用
我正试图使用Haskell DerivingVia在多参数类型类上的应用,haskell,deriving,Haskell,Deriving,我正试图使用derivigvia为具有函数依赖关系的多参数类型类的实例定义切割样板文件 我有以下类型和类别: {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE DerivingVia #-} newtype Wrapper
derivigvia
为具有函数依赖关系的多参数类型类的实例定义切割样板文件
我有以下类型和类别:
{-# 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 Wrapper2 instance MyEq (Wrapper Int) Wrapper
正如本文第6.2节所讨论的,这将查找MyEq(Wrapper Int)Wrapper2
实例,第二个参数已“更改”,但第一个参数仍然是Wrapper Int
显然,实例MyEq(Wrapper Int)Wrapper2
不存在,因为我实现了实例MyEq(Wrapper Int)Wrapper2
我不能通过创建(请参见包装器作为第一个类型参数)来“欺骗”:
因为在这种情况下,函数依赖项Wrapper t->Wrapper 2
不受尊重
通过重写eq::f a->f a->f Bool
并删除函数依赖项,我可以很容易地解决这个问题,但我希望避免更改此API。因此,首先,让我们重复一下,您希望为您派生的实例是:
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正在寻找
一个实例MyEq包装器(Wrapper2-Wrapper-Int)
,它与我们现有的匹配
提供
您可以使用关联的类型实现相同的功能:
class MyEq a where
type Result a :: Type -> Type
eq :: a -> a -> Result a Bool
使用额外参数对Wrapper2
进行相同的定义。实例变为
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)
第二种解决方案似乎需要不可判定的实例
。它们不是都需要吗?不可判定的实例是无害的,所以我不担心它。
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)