`DeriveAnyClass`和一个空实例有什么区别? [英] What is the difference between `DeriveAnyClass` and an empty instance?

查看:122
本文介绍了`DeriveAnyClass`和一个空实例有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用木薯软件包,将进行以下编译:

Using the cassava package, the following compiles:

{-# LANGUAGE DeriveGeneric #-}

import Data.Csv
import GHC.Generics

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

但是,以下内容却没有:

However, the following does not:

{-# 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找到一个实例?

This leaves me with two questions: Why isn't the second version identical to the first? And why is the compiler hoping to find an instance for ToNamedRecord Int?

推荐答案

NB :正如David在评论中指出的那样,自我撰写此文以来,GHC已得到更新.问题中编写的代码可以编译并正常工作.因此,请想象下面的所有内容都是用过去式写的.

NB: As pointed out by David in the comments, GHC has been updated since I wrote this. The code as written in the question compiles and works correctly. So just imagine everything below is written in the past tense.

GHC文档说:

实例上下文将根据相同的规则生成 在导出Eq时使用(如果类型的类型为*),或者用于 函子(如果类型为(* -> *)).例如

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 )

deriving子句将 产生

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

约束C aC (a,b)是从数据构造函数参数生成的,但是 后者简化为C a.

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

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

So, according to the Eq rules, your deriving clause generates...

instance ToNamedRecord Int => ToNamedRecord Foo where

...与...不同

... which is not the same as...

instance ToNamedRecord Foo where

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

... in that the former is only valid if there's an instance ToNamedRecord Int in scope (which is appears there isn't in your case).

但是我发现规范有些含糊.该示例应该真正生成该代码,还是应该生成instance (C a, C (a, b)) => instance C (T a b)并让求解器释放第二个约束?在您的示例中,即使对于具有完全混凝土类型的字段,它也会生成此类约束.

But I find the spec to be somewhat ambiguous. Should the example really generate that code, or should it generate instance (C a, C (a, b)) => instance C (T a b) and let the solver discharge the second constraint? It appears, in your example, that it's generating such constraints even for fields with fully-concrete types.

我会毫不犹豫地将此问题称为bug,因为它是Eq的工作方式,但是鉴于DeriveAnyClass旨在更快地编写 empty 实例,所以它看起来并不直观.

I hesitate to call this a bug, because it's how Eq works, but given that DeriveAnyClass is intended to make it quicker to write empty instances it does seem unintuitive.

这篇关于`DeriveAnyClass`和一个空实例有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆