24

使用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

4

1 回答 1

15

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


GHC 文档说:

实例上下文将根据派生时使用的相同规则Eq(如果类型的种类是*)或 Functor 的规则(如果类型的种类是(* -> *))生成。例如

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

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

deriving子句将生成

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

约束C aC (a,b)是从数据构造函数参数生成的,但后者简化为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它是为了更快地编写实例,它看起来确实不直观。

于 2016-09-07T23:10:02.367 回答