What is the difference between `DeriveAnyClass` and an empty instance?

前端 未结 1 2089
青春惊慌失措
青春惊慌失措 2021-02-18 13:03

Using the cassava package, the following compiles:

{-# LANGUAGE DeriveGeneric #-}

import Data.Csv
import GHC.Generics

data Foo = Foo { foo :: Int } deriving (G         


        
相关标签:
1条回答
  • 2021-02-18 13:32

    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.


    The GHC docs say:

    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.

    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
    

    ... 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).

    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.

    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.

    0 讨论(0)
提交回复
热议问题