Каковы ситуации, когда вы можете/не можете иметь экземпляры функтора для типа данных?
Рассматривая тип с видом * -> *, я пытаюсь найти правила и построить интуицию для того, когда вы можете и когда вы не можете иметь функтор для этого типа.
Пока что правила, которые я вижу, следующие:
- нет экземпляра
Functor
для типов контейнеров, имеющих ограничения о содержащихся в них значениях.
Пример: вы не можете иметь экземпляр Functor
для Set
, потому что Ord
требуется для содержащегося значения
- не
Functor
экземпляр для контравариантных тип данных.
Пример:
newtype Contra a = Contra (a -> Int)
Кроме этого, есть ли другие ситуации?
3 ответа:
В дополнение к вашим правилам:
- должен быть добрым
* -> *
- нет экземпляра
Functor
для типов контейнеров, которые имеют ограничения на содержащиеся значения.- нет экземпляра
Functor
для контравариантных типов данных.Я бы добавил несколько:
- естественное расширение "не для контравариантных типов": нет
Functor
экземпляра для инвариантных типов данных. напримерdata Iso a b = Iso (a -> b) (b -> a)
GADTs часто не могут иметь экземпляр
Functor
. Для пример,data Foo a where Foo :: Foo Int
Возможно, вы каким-то образом захотите объединить это в правило "только ковариант" (мне не ясно, какая дисперсия это вообще имеет) или правило "неограниченных типов контейнеров" (GADTs вводят равенства типов, которые очень похожи на ограничения).
Однако имейте в виду, что эти правила применяются к
Functor
только , не к функторам вообще. Я ожидаю, что любой глупый тип (соответствующего вида), который вы можете приготовить, будет функтором на некоторых подходящая категория, тесно связанная с Хаском.
С точки зрения теории категорий конструкторы типа Хаскелла вида
В Хаскелле морфизмы являются функциями, и отображение функций реализуется как*->*
определяют отображение объектов в категории Hask (это категория по модулю конечных вопросов, которые я собираюсь удобно игнорировать). Функтор-это отображение объектов и, что более важно, отображение морфизмов. На самом деле, это в первую очередь отображение морфизмов-отображение объектов является, в некотором смысле, побочным эффектом этого. Объекты - это всего лишь конечные точки морфизмов. Это сопоставление морфизмы должны сохранять композицию и идентичность.fmap
. Тот факт, что в Хаскелле мы начинаем с отображения объектов, немного отстает. Это работает потому, что синтаксис языка резко ограничивает возможности определения отображения объектов. Такие отображения очень регулярны и довольно часто снабжены каноническими отображениями функций. Например, алгебраические типы данных строятся с использованием продуктов и копродуктов, которые являются функциональными по своей природе (отсюда возможностьderiving Functor
автоматически). Кроме того, типы функций (категориальные экспоненты) являются функториальными во втором аргументе (и контравариантными в первом). Таким образом, пока мы используем инструменты бикартезианской замкнутой категории (продукты, копродукты и экспоненты), легко построить функториальные отображения объектов.Функтор должен быть определен для каждого объекта в данной категории., таким образом, типы данных, ограниченные классами типов (например,
Set
с ограничениемOrd
), не являютсяFunctor
s в Hask, но они могут быть функторами в подкатегории Hask. (В Haskell можно определить подкатегории вместе с их собственными функторами.)
В руководстве пользователя Haskell есть расширение
-XDerivingFunctor
. Вы можете найти полное описание здесь. Он содержит описание всех случаев, когда вывод функтора может завершиться неудачей. В этом описании есть случаи, когда алгоритм проверяет правильность вида и т. д. Но я считаю, что этот список ограничений для алгоритма является исчерпывающим.Например, другой случай, когда тип имеет вид
* -> *
но не может быть экземпляромFunctor
:Последняя переменная типа данных используется в - Ограничение XExistentialQuantification, или уточняется в GADT