Haskell 通过DeriveAnyClass进行的派生与emply实例声明的行为不同

Haskell 通过DeriveAnyClass进行的派生与emply实例声明的行为不同,haskell,Haskell,我有以下代码 {-# LANGUAGE PolyKinds, DefaultSignatures, FlexibleContexts, DeriveAnyClass, DeriveGeneric #-} {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-} module DeriveTest where import GHC.Generics class GenericClass a m

我有以下代码

{-# 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
它将具有更高类型的类型的类定义为参数。它还定义了派生该类实例的通用方法。现在,如果我像这样声明一个新的数据类型,并尝试派生
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

我不认为这应该归咎于
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
后者失败,错误与尝试使用
派生
子句派生
时相同。不幸的是,我不知道为什么GHC接受前者而拒绝后者,所以我只能猜测GHC在类型检查时对重叠实例的使用方式相当挑剔,而GHC在试图以某种方式解决
条件m
约束时,恰好心情不好


关于这一点,在上提交一个bug可能是值得的。

@dfeuer好的,这样做了。从
条件
类中删除
m
参数(因为它根本没有被使用)允许派生的实例,这是非常奇怪的。我会打开一个问题,谢谢。