На вывод Fmap для ADT

Предположим, что два новых типа определены следующим образом

type MyProductType a = (FType1 a, FType2 a)

type MyCoproductType a = Either (FType1 a) (FType2 a)

... и это FType1 а также Ftype2 оба случая Functor,

Если бы сейчас объявить MyProductType а также MyCoproductType как примеры Functor, потребуется ли компилятору явные определения для их соответствующих fmapили он может вывести эти определения из предыдущих?

Кроме того, ответ на этот вопрос зависит от реализации, или это следует из спецификации Haskell?


Для справки, этот вопрос был мотивирован попыткой разобраться в замечании в том, что я читаю. Автор сначала определяет

type Writer a = (a, String)

... а потом пишет (мой акцент)

...Writer конструктор типа является функториальным вa, Нам даже не нужно реализовыватьfmapдля этого, потому что это просто простой тип продукта.

Подчеркнутый текст - это замечание, которое я пытаюсь понять. Я думал, что это означает, что Хаскель мог сделать вывод fmapдля любого ADT, основанного на функторных типах, и, в частности, он может вывести fmap для "простого типа продукта", как Writer, но теперь я думаю, что это толкование неверно (по крайней мере, если я правильно читаю ответ Орджана Йохансена).

Что касается того, что автор имел в виду под этим предложением, сейчас я действительно понятия не имею. Может быть, все, что он имел в виду, это то, что не стоит переопределять Writer таким образом, что его функториальность может быть сделана явной, поскольку это такой "простой... тип". (Хватаясь за соломинку здесь.)

1 ответ

Решение

Во-первых, вы не можете вообще определить новые экземпляры для type синонимы, особенно не частично применяемые, как в вашем случае. Я думаю, что вы хотели определить newtype или же data вместо:

newtype MyProductType a = MP (FType1 a, FType2 a)
newtype MyCoproductType a = MC (Either (FType1 a) (FType2 a))

Стандартный Haskell ничего не говорит о производных Functor вообще автоматически, это возможно только с GHC DeriveFunctor расширение. (Или иногда GeneralizedNewtypeDeriving, но это не относится к вашим примерам, потому что вы не используете a так же, как последний аргумент внутри конструктора.)

Итак, давайте попробуем это:

{-# LANGUAGE DeriveFunctor #-}
data FType1 a = FType1 a deriving Functor
data FType2 a = FType2 a deriving Functor
newtype MyProductType a = MP (FType1 a, FType2 a) deriving Functor
newtype MyCoproductType a = MC (Either (FType1 a) (FType2 a)) deriving Functor

Мы получаем сообщение об ошибке:

Test.hs:6:76:
    Can't make a derived instance of ‘Functor MyCoproductType’:
      Constructor ‘MC’ must use the type variable only as the last argument of a data type
    In the newtype declaration for ‘MyCoproductType’

Оказывается, что GHC может вывести первые три, но не последний. Я считаю, что третий работает только потому, что кортежи имеют специальный корпус. Either не работает, хотя, потому что GHC не хранит никаких специальных знаний о том, как Either рассматривает свой первый аргумент. Это номинально математический функтор в этом аргументе, но не Хаскель Functor,

Обратите внимание, что GHC более разумно использует переменные только как последний аргумент типов, о которых известно, что Functors. Следующее работает отлично:

newtype MyWrappedType a = MW (Either (FType1 Int) (FType2 (Maybe a))) deriving Functor

Подводя итог: зависит, у GHC есть расширение для этого, но оно не всегда достаточно умное, чтобы делать то, что вы хотите.

Другие вопросы по тегам