`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在某些情况下它看起来确实不直观。
使用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 exampleinstance C a => C (a,b) where ... data T a b = MkT a (a,b) deriving( C )
The
deriving
clause will generateinstance C a => C (T a b) where {}
The constraints
C a
andC (a,b)
are generated from the data constructor arguments, but the latter simplifies toC 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在某些情况下它看起来确实不直观。