На вывод 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 более разумно использует переменные только как последний аргумент типов, о которых известно, что Functor
s. Следующее работает отлично:
newtype MyWrappedType a = MW (Either (FType1 Int) (FType2 (Maybe a))) deriving Functor
Подводя итог: зависит, у GHC есть расширение для этого, но оно не всегда достаточно умное, чтобы делать то, что вы хотите.