В чем разница между 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?


person Daniel Wagner    schedule 07.09.2016    source источник
comment
Я еще не видел, чтобы DeriveAnyClass делал что-нибудь полезное. Однако я видел, как это приводило к сбою во время компиляции. Я думаю, это глючит.   -  person dfeuer    schedule 08.09.2016
comment
В наши дни код, предложенный в этом вопросе, компилируется! Изменилось поведение компилятора - и в лучшую сторону.   -  person Daniel Wagner    schedule 14.06.2019
comment
Действительно, за последние пару лет над расширением было проделано много работы, в основном, Райаном Скоттом.   -  person dfeuer    schedule 14.06.2019


Ответы (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 a b) и позволить решателю снять второе ограничение? В вашем примере кажется, что он создает такие ограничения даже для полей с полностью конкретными типами.

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

person Benjamin Hodgson♦    schedule 07.09.2016
comment
Спасибо, это полностью объясняет! Теперь, когда вы выделили решаемую проблему (какой контекст следует дать экземпляру?), Я могу понять, почему специалисты GHC приняли такое решение, и я, тем не менее, начинаю считать это ошибкой. Условия, при которых он будет работать, кажутся невероятно конкретными, а объем работы, который он сохраняет в этих условиях, действительно кажется довольно небольшим. - person Daniel Wagner; 08.09.2016
comment
Я согласен. Большинство (все?) Классов, для которых полезен DeriveAnyClass, будут полагаться на суперкласс верхнего уровня, такой как Generic или Data, а не на рекурсивный контекст, основанный на структуре типа. Правила имеют смысл для Eq, потому что сгенерированный код сам по себе структурно рекурсивен. - person Benjamin Hodgson♦; 08.09.2016
comment
Этот ответ уже устарел. Как указывает Даниэль Вагнер в комментарии к вопросу, теперь код компилируется! Этот ответ следует, по крайней мере, обновить, чтобы указать на это и дать ссылку на более старую версию руководства, в которой все еще говорится, что Eq и Functor бизнес. В последних версиях гораздо более разумно вводить ограничения. - person dfeuer; 14.06.2019
comment
@dfeuer Спасибо, что уведомили меня, я добавил примечание в начало ответа - person Benjamin Hodgson♦; 14.06.2019