Подтвердить что ты не робот

В чем разница между "DeriveAnyClass" и пустым экземпляром?

Используя пакет 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?

4b9b3361

Ответ 1

NB: Как отметил Дэвид в комментариях, 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 a и C (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 ab) и позволить решателю выполнить второе ограничение? В вашем примере кажется, что он генерирует такие ограничения даже для полей с полностью конкретными типами.

Я не DeriveAnyClass назвать это ошибкой, потому что это как Eq работает, но учитывая, что DeriveAnyClass предназначен для того, чтобы ускорить написание пустых экземпляров, это кажется не интуитивным.