`DeriveAnyClass` 和空实例有什么区别?

What is the difference between `DeriveAnyClass` and an empty instance?

使用cassava包,编译如下:

{-# LANGUAGE DeriveGeneric #-}

import Data.Csv
import GHC.Generics

data Foo = Foo { foo :: Int } deriving (Generic)
instance ToNamedRecord Foo

但是,以下情况不会:

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}

import Data.Csv
import GHC.Generics

data Foo = Foo { foo :: Int } deriving (Generic, ToNamedRecord)

编译器报告:

test.hs:7:50:
    No instance for (ToNamedRecord Int)
      arising from the first field of ‘Foo’ (type ‘Int’)
    Possible fix:
      use a standalone 'deriving instance' declaration,
        so you can specify the instance context yourself
    When deriving the instance for (ToNamedRecord Foo)

这给我留下了两个问题:为什么第二个版本与第一个不同?为什么编译器希望找到 ToNamedRecord Int 的实例?

NB:正如 David 在评论中指出的那样,自从我写这篇文章以来,GHC 已经更新。问题中编写的代码可以编译并正常工作。所以想象一下下面的所有内容都是用过去时写的。


The GHC docs 说:

The instance context will be generated according to the same rules used when deriving Eq (if the kind of the type is *), or the rules for Functor (if the kind of the type is (* -> *)). For example

instance C a => C (a,b) where ...

data T a b = MkT a (a,b) deriving( C )

The deriving clause will generate

instance C a => C (T a b) where {}

The constraints C a and C (a,b) are generated from the data constructor arguments, but the latter simplifies to C a.

因此,根据 Eq 规则,您的 deriving 子句生成...

instance ToNamedRecord Int => ToNamedRecord Foo where

...这与...不同

instance ToNamedRecord Foo where

... 因为前者仅在范围内有 instance ToNamedRecord Int 时才有效(在您的情况下似乎没有)。

但我发现规范有些含糊不清。该示例应该真正生成该代码,还是应该生成 instance (C a, C (a, b)) => instance C (T a b) 并让求解器解除第二个约束?在您的示例中,它似乎正在为具有完全具体类型的字段生成此类约束。

我不愿将此称为错误,因为它是 Eq 的工作方式,但考虑到 DeriveAnyClass 旨在加快编写 empty在某些情况下它看起来确实不直观。