Есть ли [определение] fmap
в терминах join
?
Нет, нет. Это можно продемонстрировать, пытаясь это сделать. Предположим, нам дан конструктор произвольного типа T
и функции:
returnT :: a -> T a
joinT :: T (T a) -> T a
Исходя из этих данных, мы хотим определить:
fmapT :: (a -> b) -> T a -> T b
Итак, давайте сделаем набросок:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = tb
where
tb = undefined -- tb :: T b
Нам нужно как-то получить значение типа T b
. Сам по себе ta :: T a
не подойдет, поэтому нам нужны функции, которые выдают значения T b
. Единственными двумя кандидатами являются joinT
и returnT
. joinT
не помогает:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = joinT ttb
where
ttb = undefined -- ttb :: T (T b)
Это просто пинает банку в будущем, так как в этих обстоятельствах не требуется улучшение T (T b)
.
Мы могли бы попробовать returnT
вместо:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT b
where
b = undefined -- b :: b
Теперь нам нужно значение b
. Единственное, что может дать нам один - это f
:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT (f a)
where
a = undefined -- a :: a
А теперь мы застряли: ничто не может дать нам a
. Мы исчерпали все доступные возможности, поэтому fmapT
не может быть определено в таких терминах.
Отступление: недостаточно обмануть, используя такую функцию:
extractT :: T a -> a
С extractT
мы можем попробовать a = extractT ta
, что приведет к:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT (f (extractT ta))
Однако для fmapT
недостаточно иметь правильный тип: он также должен следовать законам функторов. В частности, fmapT id = id
должно держаться. С этим определением fmapT id
равно returnT . extractT
, что, в общем, не равно id
(большинство функторов, которые являются экземплярами как Monad
, так и Comonad
, служат примерами).
Является ли "каждая монада функтором", заключение при использовании bind
(в определении монады), но предположение при использовании join
?
«Каждая монада является функтором» - это предположение или, точнее, часть определения монады. Чтобы выбрать произвольную иллюстрацию, вот Эмили Рил, Теория категорий в контексте , с. 154
Определение 5.1.1. монада по категории C состоит из
эндофунктор T : C → C,
a единица естественное преобразование η : 1 C ⇒ T и
a умножение естественное преобразование μ : T 2 ⇒ T ,
, так что следующие диаграммы коммутируют в C C : [диаграммы законов монады]
Следовательно, монада включает в себя эндофунктор по определению. Для конструктора типа Haskell T
, который создает экземпляр Monad
, объектным отображением этого endofunctor является сам T
, а отображением морфизма является его fmap
. То, что T
будет экземпляром Functor
и, следовательно, будет иметь fmap
, в современном Haskell гарантировано, что Applicative
(и, соответственно, Functor
) является суперклассом Monad
.
Это вся история? Что касается Хаскелла. мы знаем, что liftM
существует, а также в недалеком прошлом Functor
не был суперклассом Monad
. Эти два факта - просто Хаскеллизмы? Не совсем. В классической работе Понятия вычисления и монады Эугенио Могги находит следующее определение (стр. 3):
Определение 1.2 ([Man76]) A Тройной Клейсли над категорией C является тройной (T, η, _ *) , где T : Obj ( C ) → Obj ( C ), η A : A → TA для A ∈ Obj ( C ), f *: TA → TB для f: A → TB и выполняются следующие уравнения:
- η A * = id T A
- η A ; f * = f для f: A → T B
- е *; g * = (f; g *) * для f: A → T B и g: B → T C
Важная деталь чДело в том, что T представлен просто как отображение объекта в категории C , а не как эндофунктор в C .Работая в категории Hask , это равносильно принятию конструктора типа T
без предположения, что это экземпляр Functor
.В коде мы могли бы написать это как:
class KleisliTriple t where
return :: a -> t a
(=<<) :: (a -> t b) -> t a -> t b
-- (return =<<) = id
-- (f =<<) . return = f
-- (g =<<) . (f =<<) = ((g =<<) . f =<<)
Отразить привязку в стороне, то есть определение до 12000 * AMP в Haskell.Неудивительно, что статья Могги не заставляет себя долго ждать, чтобы показать, что «существует троекратное соответствие между тройками Клейсли и монадами» (стр. 5), установив, что T может быть расширен доendofunctor (в Haskell этот шаг сводится к определению отображения морфизма liftM f m = return . f =<< m
и последующему его отображению в соответствии с законами функтора).
В общем, если вы напишите законные определения return
и (>>=)
не предполагая fmap
, вы действительно получите законную реализацию Functor
как следствие.«Существует однозначное соответствие между тройками Клейсли и монадами» является следствием определения тройки Клейсли, в то время как «монада включает эндофунктор» является частью определения монады.Соблазнительно подумать, будет ли более точным описывать то, что Хаскеллерс делал при написании Monad
экземпляров, как «создание тройки Клесили», а не «создание монады», но я воздержусь от страха быть увязшим в уныниитерминологическая педантичность - и в любом случае, теперь, когда Functor
является суперклассом Monad
, нет никаких практических причин для беспокойства по этому поводу.
bind
более "мощный", чем join
?И что будет означать «более сильный»?
Вопрос с подвохом!
Если принять за чистую монету, ответ будет положительным, если вместе с return
, (>>=)
позволяет реализовать fmap
(через liftM
, как отмечено выше), а join
- нет.Однако я не чувствую, что стоит настаивать на этом различии.Почему так?Из-за законов монады.Точно так же, как не имеет смысла говорить о законных (>>=)
без предположения return
, не имеет смысла говорить о законных join
без давления return
и fmap
.
Может сложиться впечатление, что я придаю слишком большое значение законам, используя их, чтобы связать Monad
и Functor
таким образом.Это правда, что есть случаи законов, которые включают два класса, и это применяется только к типам, которые создают их оба.Foldable
предоставляет хороший пример этого: мы можем найти следующий закон в документации Traversable
:
Экземпляры суперкласса должны удовлетворять следующему: [...]
В случае Foldable
, foldMap
должен быть эквивалентен обходу с постоянным аппликативным функтором (foldMapDefault
).
То, что этот конкретный закон не всегдаприменить это не проблема, потому что нам не нужно, чтобы характеризовать, что такое Foldable
(альтернативы включают «Foldable
- это контейнер, из которого мы можем извлечь некоторую последовательность элементов», а «Foldable
- этоконтейнер, который можно преобразовать в свободный моноид по типу элемента ").Однако с законами монады это не так: значение класса неразрывно связано со всеми тремя законами монады.