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)

これにより、2 つの疑問が残ります。2 番目のバージョンが最初のバージョンと同じでないのはなぜですか? そして、コンパイラが のインスタンスを見つけようとしているのはなぜToNamedRecord Intですか?

4

1 に答える 1

15

NB : コメントで David が指摘したように、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)してソルバーに 2 番目の制約を解放させる必要がありますか? あなたの例では、完全に具体的なタイプのフィールドであっても、そのような制約を生成しているようです。

私はこれをバグと呼ぶのをためらっています。なぜならそれがどのように機能EqするかだDeriveAnyClassからです。

于 2016-09-07T23:10:02.367 に答える