Монада - это просто моноид в категории эндофункторов, в чем проблема?

723

Кто первым сказал следующее?

Монада - это просто моноид в категории эндофункторов, в чем проблема?

И на менее важной ноте, правда ли это, и если да, то могли бы вы дать объяснение (надеюсь, что оно может быть понято кем-то, кто не имеет большого опыта в Haskell)?

Роман А. Тайчер
источник
14
Смотрите «Категории для рабочего математика»
Дон Стюарт
19
Вам не нужно понимать это, чтобы использовать монады в Haskell. С практической точки зрения они просто умный способ обойти «состояние» через какую-то подземную сантехнику.
голубой
1
Я также хотел бы добавить этот отличный пост в блоге здесь: stephendiehl.com/posts/monads.html Он не дает прямого ответа на вопрос, но, по моему мнению, Стивен великолепно связывает воедино категории и монады в Haskell. Если вы прочитали приведенные выше ответы - это должно помочь объединить два взгляда на это.
Бен Форд
3
Точнее, «для любой категории C категория [C, C] ее эндофункторов имеет моноидальную структуру, индуцированную композицией. Моноидный объект в [C, C] является монадой на C.» - из en.wikipedia.org/wiki/Monoid_%28category_theory%29. См. En.wikipedia.org/wiki/Monad_%28category_theory%29 для определения монады в теории категорий.
1
@Dmitry Функтор - это функция между категориями, с некоторыми ограничениями для хорошего поведения. Endofunctor в категории C - это просто функтор от C до самого себя. Data.Functor - это класс типов для endofunctors в категории Hask . Поскольку категория состоит из объектов и морфизмов, функтору необходимо отобразить оба. Для экземпляра f объекта Data.Functor карта объектов (типы haskell) - это сама f, а карта морфизмов (функции haskell) - fmap.
Маттис

Ответы:

797

Эта конкретная фраза написана Джеймсом Ири из его очень интересной « Краткой, неполной и в основном неправильной истории языков программирования» , в которой он вымышленно приписывает ее Филиппу Уодлеру.

Оригинальная цитата написана Сондерсом Мак Лейном в разделе «Категории для рабочего математика» , одного из основополагающих текстов теории категорий. Здесь это в контексте , который, вероятно, является лучшим местом, чтобы узнать, что именно это означает.

Но я сделаю удар. Оригинальное предложение таково:

В общем, монада в X - это просто моноид в категории эндофункторов X, с произведением ×, замененным композицией эндофункторов и единицей, установленной единичным эндофунктором.

Х здесь есть категория. Endofunctors функторы из категории в себе (что, как правило , все Functor -е, насколько функциональные программисты обеспокоены, так как они в основном имеет дело только с одной категорией; категория типов - но я отвлекся). Но вы можете представить себе другую категорию, которая относится к категории «эндофункторы на X ». Это категория, в которой объекты являются эндофункторами, а морфизмы - естественными преобразованиями.

И из этих эндофункторов, некоторые из них могут быть монадами. Какие из них монады? Именно те, которые являются моноидальными в определенном смысле. Вместо того, чтобы описать точное отображение от монад к моноидам (поскольку Mac Lane делает это намного лучше, чем я мог надеяться), я просто добавлю их соответствующие определения рядом и позволю вам сравнить:

Моноид это ...

  • Набор, С
  • Операция, •: S × S → S
  • Элемент S , e: 1 → S

... удовлетворяя этим законам:

  • (a • b) • c = a • (b • c) , для всех a , b и c в S
  • е • а = а • е = а , для всех а в S

Монада это ...

  • Endofunctor, T: X → X (в Haskell, конструктор типов * -> *с Functorэкземпляром)
  • Естественное преобразование, μ: T × T → T , где × означает композицию функтора ( μ известен как joinв Haskell)
  • Естественное преобразование, η: I → T , где I - единичный эндофунктор на X ( η известно как returnв Хаскеле)

... удовлетворяя этим законам:

  • μ ∘ Tμ = μ ∘ μT
  • μ ∘ Tη = μ ∘ ηT = 1 (тождественное естественное преобразование)

Немного прищурившись, вы сможете увидеть, что оба эти определения являются примерами одной и той же абстрактной концепции .

Том Крокетт
источник
21
спасибо за объяснение и за статью «Краткая, неполная и в основном неправильная история языков программирования». Я думал, что это может быть оттуда. Поистине один из величайших юморов программирования.
Роман А. Тайчер
6
@Jonathan: В классической формулировке моноида × означает декартово произведение множеств. Вы можете прочитать больше об этом здесь: en.wikipedia.org/wiki/Cartesian_product , но основная идея состоит в том, что элемент S × T является пара (S, T) , где s ∈ S и T ∈ T . Таким образом, сигнатура моноидального произведения •: S × S -> S в этом контексте просто означает функцию, которая принимает 2 элемента S в качестве входных данных и создает другой элемент S в качестве выходных данных.
Том Крокетт
12
@TahirHassan - В общности теории категорий мы имеем дело с непрозрачными «объектами» вместо множеств, и поэтому нет априорного понятия «элементы». Но если вы подумаете о категории « Набор», где объекты - это наборы, а стрелки - функции, элементы любого набора S находятся в взаимно однозначном соответствии с функциями из любого одноэлементного набора, равного S. То есть для любого элемент e из S , есть ровно одна функция f: 1 -> S , где 1 - любой одноэлементный набор ... (продолжение)
Том Крокетт
12
@TahirHassan 1-элементные наборы сами являются специализациями более общего теоретико-категоричного понятия «терминальные объекты»: терминальный объект - это любой объект категории, для которого есть ровно одна стрелка от любого другого объекта (вы можете проверить, что это верно для 1-элементных наборов в Set ). В теории категорий терминальные объекты просто называются 1 ; они уникальны с точностью до изоморфизма, поэтому нет смысла их различать. Итак, теперь у нас есть чисто теоретико-категоричное описание «элементов S » для любого S : это просто стрелки от 1 до S !
Том Крокетт
7
@TahirHassan - Чтобы выразить это в терминах Haskell, подумайте о том факте, что если Sэто тип, все, что вы можете сделать при написании функции, f :: () -> Sэто выбрать какой-то конкретный термин типа S(его «элемент», если хотите) и вернуть это ... вам не дали никакой реальной информации с аргументом, поэтому нет способа изменить поведение функции. Поэтому fдолжна быть постоянная функция, которая каждый раз возвращает одно и то же. ()(«Unit») является терминальным объектом категории Hask , и не случайно, что существует ровно 1 (не расходящаяся) величина, которая населяет его.
Том Крокетт
533

Интуитивно, я думаю, что сказочный математический словарь говорит о том, что:

Monoid

Моноид представляет собой набор объектов, а также способ их объединения. Хорошо известны моноиды:

  • числа, которые вы можете добавить
  • списки, которые вы можете объединить
  • наборы, которые вы можете объединить

Есть и более сложные примеры.

Кроме того, у каждого моноида есть идентичность , то есть тот элемент «без операции», который не действует, когда вы комбинируете его с чем-то другим:

  • 0 + 7 == 7 + 0 == 7
  • [] ++ [1,2,3] == [1,2,3] ++ [] == [1,2,3]
  • {} union {apple} == {apple} union {} == {apple}

Наконец, моноид должен быть ассоциативным . (вы можете уменьшить длинную строку комбинаций в любом случае, если только вы не измените порядок объектов слева направо). Дополнение в порядке ((5 + 3) +1 == 5+ (3+ 1)), но вычитание не ((5-3) -1! = 5- (3-1)).

монада

Теперь давайте рассмотрим особый вид множества и особый способ объединения объектов.

Объекты

Предположим, что ваш набор содержит объекты специального вида: функции . И эти функции имеют интересную сигнатуру: они не переносят числа в числа или строки в строки. Вместо этого каждая функция переносит число в список чисел в двухэтапном процессе.

  1. Вычислить 0 или более результатов
  2. Объедините эти результаты в один ответ как-нибудь.

Примеры:

  • 1 -> [1] (просто оберните ввод)
  • 1 -> [] (отменить ввод, обернуть небытие в списке)
  • 1 -> [2] (добавьте 1 к входу и оберните результат)
  • 3 -> [4, 6] (добавить 1 к вводу, умножить ввод на 2 и обернуть несколько результатов )

Объединение объектов

Кроме того, наш способ объединения функций является особенным. Простой способ объединить функцию - это композиция : давайте возьмем наши примеры выше и скомпонуем каждую функцию с собой:

  • 1 -> [1] -> [[1]] (дважды обернуть вход)
  • 1 -> [] -> [] (отменить ввод, обернуть пустоту в списке, дважды)
  • 1 -> [2] -> [UH-OH! ] (мы не можем «добавить 1» в список! »)
  • 3 -> [4, 6] -> [UH-OH! ] (мы не можем добавить 1 список!)

Не слишком углубляясь в теорию типов, дело в том, что вы можете объединить два целых числа, чтобы получить целое число, но вы не всегда можете составить две функции и получить функцию одного типа. (Функции с типом a -> a будут составлять, но a-> [a] не будут.)

Итак, давайте определим другой способ объединения функций. Когда мы объединяем две из этих функций, мы не хотим «оборачивать» результаты.

Вот что мы делаем. Когда мы хотим объединить две функции F и G, мы следуем этому процессу (называемому привязкой ):

  1. Вычислите «результаты» из F, но не объединяйте их.
  2. Вычислите результаты применения G к каждому из результатов F по отдельности, получая коллекцию результатов.
  3. Сгладьте 2-уровневую коллекцию и объедините все результаты.

Возвращаясь к нашим примерам, давайте объединим (свяжем) функцию с самой собой, используя этот новый способ «связывания» функций:

  • 1 -> [1] -> [1] (дважды обернуть вход)
  • 1 -> [] -> [] (отменить ввод, обернуть пустоту в списке, дважды)
  • 1 -> [2] -> [3] (добавьте 1, затем снова добавьте 1 и оберните результат.)
  • 3 -> [4,6] -> [5,8,7,12] (добавьте 1 к вводу, а также умножьте ввод на 2, сохраняя оба результата, затем сделайте все снова для обоих результатов, а затем оберните окончательный вариант результаты в списке.)

Этот более сложный способ объединения функций является ассоциативным (следуя тому, как составление функций ассоциативно, когда вы не делаете причудливую упаковку).

Связывая все это вместе,

  • Монада - это структура, которая определяет способ объединения (результатов) функций,
  • аналогично тому, как моноид является структурой, которая определяет способ объединения объектов,
  • где метод комбинации является ассоциативным,
  • и где есть специальное «неоперативное», которое может быть объединено с чем-то, что приведет к чему-то неизменному.

Ноты

Есть много способов «обернуть» результаты. Вы можете создать список, или набор, или отбросить все, кроме первого результата, отметив, что результатов нет, прикрепить коляску состояний, распечатать сообщение журнала и т. Д. И т. Д.

Я немного расстроился с определениями в надежде донести основную идею до интуитивно понятной.

Я немного упростил ситуацию, настаивая на том, что наша монада работает с функциями типа a -> [a] . На самом деле, монады работают с функциями типа a -> mb , но обобщение - это своего рода техническая деталь, которая не является основным понятием.

misterbee
источник
22
Это хорошее объяснение того, как каждая монада представляет собой категорию ( вы демонстрируете категорию Клейсли - есть также категория Эйленберга-Мура). Но из-за того, что вы не можете составить любые две стрелки Клейсли a -> [b]и c -> [d](вы можете сделать это только если b= c), это не совсем описывает моноид. На самом деле вы описали операцию выравнивания, а не композицию функций, которая является «оператором моноида».
Том Крокетт
6
Конечно, если вы ограничиваете монаду только одним типом, т.е. если вы разрешаете стрелы Клейсли только в форме a -> [a], это будет моноид (потому что вы будете уменьшать категорию Клейсли до одного объекта и любую категорию только с одним объектом). по определению является моноидом!), но он не охватил бы всей общности монады.
Том Крокетт
5
На последнем замечании, это помогает помнить, что a -> [a] - это просто -> [] a. ([] тоже просто конструктор типов.) И поэтому его можно не только рассматривать как -> mb, но [] действительно является экземпляром класса Monad.
Evi1M4chine
8
Это лучшее и наиболее вежливое объяснение монад и их математического фона моноидов, с которыми я столкнулся буквально за несколько недель. Это то, что должно быть напечатано в каждой книге на Haskell, когда дело доходит до монад, руками вниз. UPVOTE! Может быть, в дальнейшем получу информацию, что монады реализованы как параметризованные экземпляры классов типов, которые помещают в сообщение все, что помещено в них в haskell. (По крайней мере, теперь я их так понял. Поправьте меня, если я ошибаюсь. См. Haskell.org/haskellwiki/What_a_Monad_is_not )
sjas
1
Это фантастика - это единственное объяснение, которое я понял достаточно хорошо, чтобы иметь возможность объяснить это кому-то еще ... Но я все еще не понимаю, почему это ценный способ думать о чем-либо. :(
Адам Барнс
84

Во-первых, расширения и библиотеки, которые мы собираемся использовать:

{-# LANGUAGE RankNTypes, TypeOperators #-}

import Control.Monad (join)

Из них RankNTypesединственный, который абсолютно необходим для нижеследующего. Однажды я написал объяснение того, RankNTypesчто некоторые люди считают полезным , поэтому я буду ссылаться на это.

Цитируя отличный ответ Тома Крокетта , мы имеем:

Монада это ...

  • Эндофунктор, T: X -> X
  • Естественное преобразование, μ: T × T -> T , где × означает композицию функтора.
  • Естественное преобразование η: I -> T , где I - единичный эндофунктор на X

... удовлетворяя этим законам:

  • μ (μ (T × T) × T)) = μ (T × μ (T × T))
  • μ (η (T)) = T = μ (T (η))

Как мы можем перевести это на код на Haskell? Что ж, давайте начнем с понятия естественной трансформации :

-- | A natural transformations between two 'Functor' instances.  Law:
--
-- > fmap f . eta g == eta g . fmap f
--
-- Neat fact: the type system actually guarantees this law.
--
newtype f :-> g =
    Natural { eta :: forall x. f x -> g x }

Тип формы f :-> gаналогичен типу функции, но вместо того, чтобы думать о ней как о функции между двумя типами (типа *), думать о ней как о морфизме между двумя функторами (каждый из которых является родом * -> *). Примеры:

listToMaybe :: [] :-> Maybe
listToMaybe = Natural go
    where go [] = Nothing
          go (x:_) = Just x

maybeToList :: Maybe :-> []
maybeToList = Natural go
    where go Nothing = []
          go (Just x) = [x]

reverse' :: [] :-> []
reverse' = Natural reverse

По сути, в Haskell естественные преобразования - это функции из f xодного типа в другой g x, так что xпеременная типа «недоступна» для вызывающей стороны. Так, например, sort :: Ord a => [a] -> [a]нельзя превратить в естественную трансформацию, потому что она «требовательна» к тому, для каких типов мы можем создавать экземпляры a. Один интуитивный способ, которым я часто пользуюсь, чтобы думать об этом, заключается в следующем:

  • Функтор - это способ оперировать содержимым чего-либо, не касаясь структуры .
  • Естественная трансформация - это способ воздействовать на структуру чего-либо, не касаясь и не глядя на содержание .

Теперь, со всем этим, давайте рассмотрим пункты определения.

Первый пункт - это «endofunctor, T: X -> X ». Ну, каждый Functorв Haskell является эндофунктором в том, что люди называют «категорией Hask», чьи объекты являются типами Haskell (вида *) и чьи морфизмы являются функциями Haskell. Это звучит как сложное утверждение, но на самом деле оно очень тривиально. Все это означает, что a Functor f :: * -> *дает вам возможность создать тип f a :: *для любого a :: *и функцию fmap f :: f a -> f bиз любого f :: a -> b, и что они подчиняются законам функторов.

Второе предложение: Identityфунктор в Haskell (который поставляется с платформой, так что вы можете просто импортировать его) определяется следующим образом:

newtype Identity a = Identity { runIdentity :: a }

instance Functor Identity where
    fmap f (Identity a) = Identity (f a)

Таким образом, естественное преобразование η: I -> T из определения Тома Крокетта может быть записано так для любого Monadслучая t:

return' :: Monad t => Identity :-> t
return' = Natural (return . runIdentity)

Третье предложение: состав двух функторов в Haskell может быть определен следующим образом (который также поставляется с платформой):

newtype Compose f g a = Compose { getCompose :: f (g a) }

-- | The composition of two 'Functor's is also a 'Functor'.
instance (Functor f, Functor g) => Functor (Compose f g) where
    fmap f (Compose fga) = Compose (fmap (fmap f) fga)

Поэтому естественное преобразование μ: T × T -> T из определения Тома Крокетта можно записать так:

join' :: Monad t => Compose t t :-> t
join' = Natural (join . getCompose)

Утверждение, что это моноид в категории эндофункторов, означает, что Compose(частично примененное только к его первым двум параметрам) является ассоциативным, и это Identityявляется его единичным элементом. Т.е. справедливы следующие изоморфизмы:

  • Compose f (Compose g h) ~= Compose (Compose f g) h
  • Compose f Identity ~= f
  • Compose Identity g ~= g

Это очень легко доказать , потому что Composeи Identityоба определены как newtype, и Haskell отчеты определяют семантикуnewtype как изоморфизм между типом определяется и тип аргумента newtypeконструктора «ы данных. Итак, например, давайте докажем Compose f Identity ~= f:

Compose f Identity a
    ~= f (Identity a)                 -- newtype Compose f g a = Compose (f (g a))
    ~= f a                            -- newtype Identity a = Identity a
Q.E.D.
Луис Касильяс
источник
В Naturalновом типе я не могу понять, что (Functor f, Functor g)делает ограничение. Могли бы вы объяснить?
dfeuer
@dfeuer Ничего существенного не делает.
Луис Касильяс
1
@ LuisCasillas Я снял эти Functorограничения, так как они не кажутся необходимыми. Если вы не согласны, добавьте их обратно.
Лямбда Фея
Можете ли вы уточнить, что формально означает, что произведение функторов следует рассматривать как композицию? В частности, каковы морфизмы проекции для композиции функторов? Я предполагаю, что произведение определено только для функтора F против самого себя, F x F и только когда joinопределено. И это joinморфизм проекции. Но я не уверен.
tksfz
6

Примечание: нет, это не правда. В какой-то момент был дан комментарий к этому ответу от самого Дана Пипони, в котором говорилось, что причина и следствие здесь были совершенно противоположными, что он написал свою статью в ответ на шутку Джеймса Ири. Но, похоже, он был удален, возможно, каким-то навязчивым тидиром.

Ниже мой оригинальный ответ.


Вполне возможно, что Ири прочитал статью « От моноидов к монадам» - пост, в котором Дэн Пипони (sigfpe) выводит монады из моноидов в Хаскеле, с большим обсуждением теории категорий и явным упоминанием «категории эндофункторов на Хаске ». В любом случае, любой, кто интересуется, что значит монада в категории эндофункторов, может извлечь пользу из прочтения этого вывода.

Hobbs
источник
1
«Возможно, каким-то навязчивым тидиром» - или, как мы наивно ссылаемся на них на этом сайте, модератором :-).
halfer
6

Я пришел к этому посту, чтобы лучше понять вывод печально известной цитаты из « Теории категорий Мак Лэйна для рабочего математика» .

При описании того, что является чем-то, часто одинаково полезно описать, что это не так.

Тот факт, что Mac Lane использует описание для описания монады, можно предположить, что она описывает нечто уникальное для монад. Потерпите меня. Чтобы развить более широкое понимание заявления, я полагаю, что необходимо дать понять, что он не описывает нечто уникальное для монад; утверждение одинаково описывает Аппликатив и Стрелки среди других. По той же причине у нас может быть два моноида на Int (Sum и Product), у нас может быть несколько моноидов на X в категории эндофункторов. Но есть еще больше сходства.

И Monad, и Applicative соответствуют критериям:

  • endo => любая стрелка или морфизм, который начинается и заканчивается в одном и том же месте
  • functor => любая стрелка или морфизм между двумя категориями

    (например, изо дня в день Tree a -> List b, но в категории Tree -> List)

  • моноид => один объект; т. е. один тип, но в этом контексте только в отношении внешнего уровня; Итак, мы не можем иметь Tree -> List, только List -> List.

В операторе используется «Категория ...» Это определяет область действия оператора. В качестве примера Категория Functor описывает область действия f * -> g *, Any functor -> Any functorнапример, Tree * -> List *или Tree * -> Tree *.

То, что категорическое утверждение не указывает, описывает, где что-либо и все разрешено .

В этом случае внутри функторов * -> *aka a -> bне указано, что означает Anything -> Anything including Anything else. Когда мое воображение переходит к Int -> String, оно также включает Integer -> Maybe Intили даже Maybe Double -> Either String Intгде a :: Maybe Double; b :: Either String Int.

Таким образом, утверждение сводится к следующему:

  • область действия функтора :: f a -> g b(т. е. любой параметризованный тип для любого параметризованного типа)
  • endo + функтор :: f a -> f b(т. е. любой один параметризованный тип к тому же параметризованному типу) ... по-другому,
  • моноид в категории эндофунктор

Итак, где же сила этой конструкции? Чтобы оценить всю динамику, мне нужно было увидеть, что типичные рисунки моноида (один объект с чем-то вроде стрелки-идентификатора :: single object -> single object) не иллюстрируют, что мне разрешено использовать стрелку, параметризованную любым количеством значений моноидов, из объекта одного типа, разрешенного в Monoid. Определение эквивалентности стрелки endo, ~ identity игнорирует значение типа функтора, а также тип и значение самого внутреннего слоя «полезной нагрузки». Таким образом, эквивалентность возвращается trueв любой ситуации, когда совпадают функторные типы (например, Nothing -> Just * -> Nothingэквивалентно тому, Just * -> Just * -> Just *что они оба Maybe -> Maybe -> Maybe).

Боковая панель: ~ снаружи является концептуальным, но является самым левым символом в f a. Он также описывает, что «Хаскелл» читает первым (большая картина); так что тип "снаружи" по отношению к значению типа. Взаимосвязь между слоями (цепочкой ссылок) в программировании нелегко установить в категории. Категория набора используется для описания типов (Int, Strings, Maybe Int и т. Д.), Которые включают в себя категорию функторов (параметризованные типы). Цепочка ссылок: тип функтора, значения функтора (элементы набора этого функтора, например, Nothing, Just) и, в свою очередь, все остальное, на которое указывает каждое значение функтора. В категории отношения описываются по-разному, например, return :: a -> m aсчитается естественным преобразованием одного функтора в другой, отличным от всего, что упоминалось до сих пор.

Возвращаясь к основному потоку, в общем, для любого определенного тензорного произведения и нейтрального значения, в итоге утверждение описывает удивительно мощную вычислительную конструкцию, порожденную ее парадоксальной структурой:

  • снаружи он выглядит как единый объект (например, :: List); статический
  • но внутри, допускает много динамики
    • любое количество значений того же типа (например, Empty | ~ NonEmpty), что и fodder для функций любой арности. Тензорный продукт уменьшит любое количество входных данных до единого значения ... для внешнего уровня (~ foldкоторый ничего не говорит о полезной нагрузке)
    • бесконечный диапазон как типа, так и значений для самого внутреннего слоя

В Haskell важно уточнить применимость этого утверждения. Мощь и универсальность этой конструкции не имеет абсолютно никакого отношения к монаде как таковой . Другими словами, конструкция не полагается на то, что делает монаду уникальной.

При попытке выяснить, следует ли создавать код с общим контекстом для поддержки вычислений, которые зависят друг от друга, по сравнению с вычислениями, которые могут выполняться параллельно, это печально известное утверждение, как бы оно ни описывалось, не является контрастом между выбором Аппликатив, Стрелы и Монады, а скорее это описание того, насколько они одинаковы. Для данного решения утверждение является спорным.

Это часто неправильно понимают. Утверждение далее описывается join :: m (m a) -> m aкак тензорное произведение для моноидального эндофунктора. Однако в нем не сформулировано, как в контексте этого утверждения (<*>)также можно было бы выбрать. Это действительно пример шести / полдюжины. Логика объединения значений абсолютно одинакова; один и тот же вход генерирует одинаковый выход из каждого (в отличие от моноидов Sum и Product для Int, потому что они генерируют разные результаты при объединении Ints).

Итак, резюмируем: моноид в категории эндофункторов описывает:

   ~t :: m * -> m * -> m *
   and a neutral value for m *

(<*>)и (>>=)оба обеспечивают одновременный доступ к двум mзначениям для вычисления единственного возвращаемого значения. Логика, используемая для вычисления возвращаемого значения, точно такая же. Если бы не различные формы функций, которые они параметризуют (по f :: a -> bсравнению с k :: a -> m b), и положение параметра с одним и тем же типом возврата вычислений (т. a -> b -> bЕ. По сравнению b -> a -> bс каждым соответственно), я подозреваю, что мы могли бы параметризовать моноидальную логику, тензорное произведение, для повторного использования в обоих определениях. В качестве упражнения, чтобы понять суть, попробуйте и осуществите ~t, и вы в конечном итоге (<*>)и в (>>=)зависимости от того, как вы решите его определить forall a b.

Если моя последняя точка концептуально верна как минимум, тогда она объясняет точное и единственное вычислительное различие между Applicative и Monad: функции, которые они параметризуют. Другими словами, разница является внешней по отношению к реализации этих классов типов.

В заключение, по моему собственному опыту, печально известная цитата Мак Лэйна предоставила мне замечательный мем «goto», который мне мог бы указать при навигации по Category, чтобы лучше понять идиомы, используемые в Haskell. Ему удается захватить всю мощь мощных вычислительных возможностей, которые чудесно доступны в Haskell.

Тем не менее, есть ирония в том, что я впервые неправильно понял применимость заявления за пределами монады, и то, что я надеюсь передать здесь. Все, что он описывает, оказывается похожим между Applicative и Monads (и Arrows среди других). Чего не говорится, так это небольшого, но полезного различия между ними.

- E

Эхо Эдмунда
источник
5

Ответы здесь отлично справляются с определением моноидов и монад, однако они все еще не отвечают на вопрос:

И на менее важной ноте, правда ли это, и если да, то могли бы вы дать объяснение (надеюсь, что оно может быть понято кем-то, кто не имеет большого опыта в Haskell)?

Суть вопроса, который здесь отсутствует, - это другое понятие «моноид», точнее , так называемая категоризация - понятие «моноид» в моноидальной категории. К сожалению, сама книга Мак Лэйна делает ее очень запутанной :

В общем, монада X- это просто моноид в категории эндофункторов X, продукт ×заменяется составом эндофункторов, а единица устанавливается единичным эндофунктором.

Главное замешательство

Почему это сбивает с толку? Потому что он не определяет, что такое «моноид в категории эндофункторов» X. Вместо этого в этом предложении предлагается взять моноид внутри множества всех эндофункторов вместе с композицией функторов в качестве двоичной операции и функтором тождеств в качестве моноидальной единицы. Который прекрасно работает и превращает в моноид любое подмножество эндофункторов, которое содержит тождественный функтор и закрыто под композицией функторов.

Тем не менее, это не правильное толкование, которое книга не может прояснить на этом этапе. Монада f- это фиксированный эндофунктор, а не подмножество эндофункторов, замкнутых в композиции. Обычной конструкцией является использование fдля создания моноида путем взятия множества kразложенных композиций.f^k = f(f(...)) из fс самими собой, в том числе , k=0что соответствует идентичности f^0 = id. И теперь набор Sвсех этих полномочий для всех k>=0действительно является моноидом «с произведением ×, замененным композицией эндофункторов и единицей, установленной единичным эндофунктором».

И все еще:

  • Это моноид S может быть определен для любого функтора fили даже буквально для любой собственной карты X. Это моноид, созданный f.
  • Моноидальная структура S заданная композицией функторов и функтором идентичности, не имеет ничего общего с тем, fбыть или не быть монадой.

И чтобы сделать вещи более запутанными, определение «моноид в моноидальной категории» приходит позже в книге, как вы можете видеть из оглавления . И все же понимание этого понятия абсолютно необходимо для понимания связи с монадами.

(Строгие) моноидальные категории

Переходя к главе VII на моноидах (который приходит позже , чем глава VI на монадах), мы находим определение так называемой строгой категории моноидальной как тройные (B, *, e), где Bкатегорию, *: B x B-> Bв бифунктора (функтор по отношению к каждому компоненту с другим компонентом фиксированного ) и eявляется единичным объектом B, удовлетворяющим законам ассоциативности и единицы:

(a * b) * c = a * (b * c)
a * e = e * a = a

для любых объектов a,b,cиз B, и того же тождества для любых морфизмов a,b,cс eзаменено id_e, тождественным морфизмом e. Теперь полезно отметить, что в нашем интересном случае, где Bесть категория эндофункторов Xс естественными преобразованиями в виде морфизмов, *композиция функторов и eфунктор тождества, все эти законы выполняются, что можно непосредственно проверить.

Далее в книге дается определение «расслабленной» моноидальной категории , где законы выполняются только по модулю некоторых фиксированных естественных преобразований, удовлетворяющих так называемым отношениям когерентности. , что, однако, не важно для наших случаев категорий эндофункторов.

Моноиды в моноидальных категориях

Наконец, в разделе 3 «Моноиды» главы VII дано фактическое определение:

Моноид cв моноидальной категории (B, *, e)- объект Bс двумя стрелками (морфизмы)

mu: c * c -> c
nu: e -> c

сделать 3 диаграммы коммутативными. Напомним, что в нашем случае это морфизмы в категории эндофункторов, которые являются естественными преобразованиями, соответствующими точно joinи returnдля монады. Связь становится еще более ясной, когда мы делаем композицию *более явной, заменяя ее c * cтем c^2, где cнаходится наша монада.

Наконец, обратите внимание, что 3 коммутативные диаграммы (в определении моноида в моноидальной категории) написаны для общих (нестрогих) моноидальных категорий, в то время как в нашем случае все естественные преобразования, возникающие как часть моноидальной категории, на самом деле являются тождествами. Это сделает диаграммы точно такими же, как в определении монады, что сделает соответствие полным.

Вывод

Таким образом, любая монада по определению является эндофунктором, следовательно, объектом в категории эндофункторов, где монады joinи returnоператоры удовлетворяют определению моноида в этой конкретной (строгой) моноидальной категории . Наоборот, любой моноид в моноидальной категории эндофункторов по определению является тройкой, (c, mu, nu)состоящей из объекта и двух стрелок, например, в нашем случае естественных преобразований, удовлетворяющих тем же законам, что и монада.

Наконец, обратите внимание на ключевое различие между (классическими) моноидами и более общими моноидами в моноидальных категориях. Две стрелки muи nuвыше больше не являются бинарной операцией и единым целым в наборе. Вместо этого у вас есть один фиксированный endofunctor c. Композиция функтора *и один только функтор идентичности не обеспечивают полной структуры, необходимой для монады, несмотря на это запутанное замечание в книге.

Другой подход был бы сравнить со стандартным моноиде Cвсех самостоятельных отображений множества A, где бинарная операция является композиция, которая может быть замечена , чтобы отобразить стандартный декартово произведение C x Cв C. Переходя к категоризированному моноиду, мы заменяем декартово произведение xна функторную композицию *, и бинарная операция заменяется естественным преобразованием muиз c * cв c, то есть набором joinоператоров.

join: c(c(T))->c(T)

для каждого объекта T(введите в программировании). И элементы идентичности в классических моноидах, которые могут быть идентифицированы с изображениями карт из фиксированного одноточечного множества, заменяются набором returnоператоров

return: T->c(T) 

Но теперь нет больше декартовых произведений, поэтому нет пар элементов и, следовательно, нет бинарных операций.

Дмитрий Зайцев
источник
Итак, каков ваш ответ на «это правда» часть вопроса? Правда ли, что монада является моноидом в категории эндофункторов? И если да, какова связь между понятием теории категорий моноида и алгебраического моноида (множество с ассоциативным умножением и единицей)?
Александр Белопольский