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

时间:2016-09-07 21:06:15

标签: haskell generics deriving

使用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的实例?

1 个答案:

答案 0 :(得分:15)

The GHC docs说:

  

将根据相同的规则生成实例上下文   在导出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旨在使编写实例更快,它似乎不直观