【问题标题】:Deriving via DeriveAnyClass not behaving the same as an emply instance declaration通过 DeriveAnyClass 派生的行为与 emply 实例声明不同
【发布时间】:2018-11-06 11:53:25
【问题描述】:

我有以下代码

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

【问题讨论】:

  • @dfeuer 好的,做到了。

标签: haskell


【解决方案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

如果您尝试使用deriving 子句派生Class,后者会失败并出现相同的错误。不幸的是,我不知道为什么 GHC 接受前者而拒绝后者,所以我只能推测 G​​HC 在类型检查时对如何使用重叠实例相当挑剔,而 GHC 在尝试解决Condition a m 以某种方式约束。

可能值得在GHC issue tracker 上提交一个关于此的错误。

【讨论】:

  • Condition 类中删除m 参数(因为它根本没有使用)允许派生实例,这很奇怪。我会打开一个问题,谢谢。
猜你喜欢
  • 2012-10-02
  • 1970-01-01
  • 2019-09-15
  • 2015-03-04
  • 2020-04-07
  • 2011-04-02
  • 1970-01-01
  • 2015-04-11
  • 2022-09-30
相关资源
最近更新 更多