我有以下代码
{-# LANGUAGE PolyKinds, DefaultSignatures, FlexibleContexts, DeriveAnyClass, DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
module DeriveTest where
import GHC.Generics
class GenericClass a m where
instance GenericClass f m => GenericClass (M1 i c f) m
instance Condition a m => GenericClass (K1 i a) m
class Condition (a :: k) (m :: * -> *) where
instance (Condition a m, Condition b m) => Condition (a b) m
instance {-# OVERLAPPABLE #-} Condition (a :: k) m
class Class (e :: (* -> *) -> *) where
classF :: e m -> ()
default classF :: GenericClass (Rep (e m)) m => e m -> ()
classF = undefined
它定义了具有较高kinded类型作为参数的类型的类Class
。它还定义了派生该类实例的通用方法。现在,如果我声明一个这样的新数据类型,并尝试派生Class
的实例
data T a m = T
{ field :: a }
deriving (Generic, Class)
我收到以下错误:
* Overlapping instances for Condition a m
arising from the 'deriving' clause of a data type declaration
Matching instances:
instance [overlappable] forall k (a :: k) (m :: * -> *).
Condition a m
instance forall k1 k2 (a :: k1 -> k2) (m :: * -> *) (b :: k1).
(Condition a m, Condition b m) =>
Condition (a b) m
(The choice depends on the instantiation of `a, m'
To pick the first instance above, use IncoherentInstances
when compiling the other instance declarations)
* When deriving the instance for (Class (T a))
|
22 | deriving (Generic, Class)
| ^^^^^
错误有点合理,因为我猜。该实例确实依赖于a
的实例化。但是,如果我只是写一个像这样的空实例:
data T a m = T
{ field :: a }
deriving (Generic)
instance Class (T a) -- works
有效。为什么?如何使它与派生语句的行为相同?
这是使用GHC 8.2.2
答案 0 :(得分:1)
我认为不应该责怪DeriveAnyClass
。我认为真正的罪魁祸首是GHC围绕重叠实例的不可预测的行为。为了明白我的意思,让我们将classF
的默认实现分解为它自己的函数:
class Class (e :: (* -> *) -> *) where
classF :: e m -> ()
default classF :: GenericClass (Rep (e m)) m => e m -> ()
classF = classFDefault
classFDefault :: forall (e :: (* -> *) -> *) (m :: * -> *).
GenericClass (Rep (e m)) m => e m -> ()
classFDefault = undefined
现在,根据您对T
:
data T a m = T
{ field :: a }
deriving (Generic)
观察这个类型检查:
instance Class (T a) where
classF = classFDefault
但这不是!
classFT :: forall a (m :: * -> *).
T a m -> ()
classFT = classFDefault
如果您尝试使用Class
子句派生deriving
,则后者失败并返回相同的错误。不幸的是,我不知道为什么GHC接受前者而拒绝后者,所以我只能猜测GHC对于在类型检查时如何使用重叠实例相当挑剔,并且GHC在尝试解决时遇到了不良情绪Condition a m
以某种方式约束。
可能值得在GHC issue tracker上提交有关此问题的错误。