Кто первым сказал следующее?
Монада - это просто моноид в категории эндофункторов, в чем проблема?
И на менее важной ноте, правда ли это, и если да, то могли бы вы дать объяснение (надеюсь, что оно может быть понято кем-то, кто не имеет большого опыта в Haskell)?
haskell
monads
category-theory
monoids
Роман А. Тайчер
источник
источник
Ответы:
Эта конкретная фраза написана Джеймсом Ири из его очень интересной « Краткой, неполной и в основном неправильной истории языков программирования» , в которой он вымышленно приписывает ее Филиппу Уодлеру.
Оригинальная цитата написана Сондерсом Мак Лейном в разделе «Категории для рабочего математика» , одного из основополагающих текстов теории категорий. Здесь это в контексте , который, вероятно, является лучшим местом, чтобы узнать, что именно это означает.
Но я сделаю удар. Оригинальное предложение таково:
Х здесь есть категория. Endofunctors функторы из категории в себе (что, как правило , все
Functor
-е, насколько функциональные программисты обеспокоены, так как они в основном имеет дело только с одной категорией; категория типов - но я отвлекся). Но вы можете представить себе другую категорию, которая относится к категории «эндофункторы на X ». Это категория, в которой объекты являются эндофункторами, а морфизмы - естественными преобразованиями.И из этих эндофункторов, некоторые из них могут быть монадами. Какие из них монады? Именно те, которые являются моноидальными в определенном смысле. Вместо того, чтобы описать точное отображение от монад к моноидам (поскольку Mac Lane делает это намного лучше, чем я мог надеяться), я просто добавлю их соответствующие определения рядом и позволю вам сравнить:
Моноид это ...
... удовлетворяя этим законам:
Монада это ...
* -> *
сFunctor
экземпляром)join
в Haskell)return
в Хаскеле)... удовлетворяя этим законам:
Немного прищурившись, вы сможете увидеть, что оба эти определения являются примерами одной и той же абстрактной концепции .
источник
S
это тип, все, что вы можете сделать при написании функции,f :: () -> S
это выбрать какой-то конкретный термин типаS
(его «элемент», если хотите) и вернуть это ... вам не дали никакой реальной информации с аргументом, поэтому нет способа изменить поведение функции. Поэтомуf
должна быть постоянная функция, которая каждый раз возвращает одно и то же.()
(«Unit») является терминальным объектом категории Hask , и не случайно, что существует ровно 1 (не расходящаяся) величина, которая населяет его.Интуитивно, я думаю, что сказочный математический словарь говорит о том, что:
Monoid
Моноид представляет собой набор объектов, а также способ их объединения. Хорошо известны моноиды:
Есть и более сложные примеры.
Кроме того, у каждого моноида есть идентичность , то есть тот элемент «без операции», который не действует, когда вы комбинируете его с чем-то другим:
Наконец, моноид должен быть ассоциативным . (вы можете уменьшить длинную строку комбинаций в любом случае, если только вы не измените порядок объектов слева направо). Дополнение в порядке ((5 + 3) +1 == 5+ (3+ 1)), но вычитание не ((5-3) -1! = 5- (3-1)).
монада
Теперь давайте рассмотрим особый вид множества и особый способ объединения объектов.
Объекты
Предположим, что ваш набор содержит объекты специального вида: функции . И эти функции имеют интересную сигнатуру: они не переносят числа в числа или строки в строки. Вместо этого каждая функция переносит число в список чисел в двухэтапном процессе.
Примеры:
Объединение объектов
Кроме того, наш способ объединения функций является особенным. Простой способ объединить функцию - это композиция : давайте возьмем наши примеры выше и скомпонуем каждую функцию с собой:
Не слишком углубляясь в теорию типов, дело в том, что вы можете объединить два целых числа, чтобы получить целое число, но вы не всегда можете составить две функции и получить функцию одного типа. (Функции с типом a -> a будут составлять, но a-> [a] не будут.)
Итак, давайте определим другой способ объединения функций. Когда мы объединяем две из этих функций, мы не хотим «оборачивать» результаты.
Вот что мы делаем. Когда мы хотим объединить две функции F и G, мы следуем этому процессу (называемому привязкой ):
Возвращаясь к нашим примерам, давайте объединим (свяжем) функцию с самой собой, используя этот новый способ «связывания» функций:
Этот более сложный способ объединения функций является ассоциативным (следуя тому, как составление функций ассоциативно, когда вы не делаете причудливую упаковку).
Связывая все это вместе,
Ноты
Есть много способов «обернуть» результаты. Вы можете создать список, или набор, или отбросить все, кроме первого результата, отметив, что результатов нет, прикрепить коляску состояний, распечатать сообщение журнала и т. Д. И т. Д.
Я немного расстроился с определениями в надежде донести основную идею до интуитивно понятной.
Я немного упростил ситуацию, настаивая на том, что наша монада работает с функциями типа a -> [a] . На самом деле, монады работают с функциями типа a -> mb , но обобщение - это своего рода техническая деталь, которая не является основным понятием.
источник
a -> [b]
иc -> [d]
(вы можете сделать это только еслиb
=c
), это не совсем описывает моноид. На самом деле вы описали операцию выравнивания, а не композицию функций, которая является «оператором моноида».a -> [a]
, это будет моноид (потому что вы будете уменьшать категорию Клейсли до одного объекта и любую категорию только с одним объектом). по определению является моноидом!), но он не охватил бы всей общности монады.Во-первых, расширения и библиотеки, которые мы собираемся использовать:
Из них
RankNTypes
единственный, который абсолютно необходим для нижеследующего. Однажды я написал объяснение того,RankNTypes
что некоторые люди считают полезным , поэтому я буду ссылаться на это.Цитируя отличный ответ Тома Крокетта , мы имеем:
Как мы можем перевести это на код на Haskell? Что ж, давайте начнем с понятия естественной трансформации :
Тип формы
f :-> g
аналогичен типу функции, но вместо того, чтобы думать о ней как о функции между двумя типами (типа*
), думать о ней как о морфизме между двумя функторами (каждый из которых является родом* -> *
). Примеры:По сути, в Haskell естественные преобразования - это функции из
f x
одного типа в другойg x
, так чтоx
переменная типа «недоступна» для вызывающей стороны. Так, например,sort :: Ord a => [a] -> [a]
нельзя превратить в естественную трансформацию, потому что она «требовательна» к тому, для каких типов мы можем создавать экземплярыa
. Один интуитивный способ, которым я часто пользуюсь, чтобы думать об этом, заключается в следующем:Теперь, со всем этим, давайте рассмотрим пункты определения.
Первый пункт - это «endofunctor, T: X -> X ». Ну, каждый
Functor
в Haskell является эндофунктором в том, что люди называют «категорией Hask», чьи объекты являются типами Haskell (вида*
) и чьи морфизмы являются функциями Haskell. Это звучит как сложное утверждение, но на самом деле оно очень тривиально. Все это означает, что aFunctor f :: * -> *
дает вам возможность создать типf a :: *
для любогоa :: *
и функциюfmap f :: f a -> f b
из любогоf :: a -> b
, и что они подчиняются законам функторов.Второе предложение:
Identity
функтор в Haskell (который поставляется с платформой, так что вы можете просто импортировать его) определяется следующим образом:Таким образом, естественное преобразование η: I -> T из определения Тома Крокетта может быть записано так для любого
Monad
случаяt
:Третье предложение: состав двух функторов в Haskell может быть определен следующим образом (который также поставляется с платформой):
Поэтому естественное преобразование μ: T × T -> T из определения Тома Крокетта можно записать так:
Утверждение, что это моноид в категории эндофункторов, означает, что
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
:источник
Natural
новом типе я не могу понять, что(Functor f, Functor g)
делает ограничение. Могли бы вы объяснить?Functor
ограничения, так как они не кажутся необходимыми. Если вы не согласны, добавьте их обратно.join
определено. И этоjoin
морфизм проекции. Но я не уверен.Примечание: нет, это не правда. В какой-то момент был дан комментарий к этому ответу от самого Дана Пипони, в котором говорилось, что причина и следствие здесь были совершенно противоположными, что он написал свою статью в ответ на шутку Джеймса Ири. Но, похоже, он был удален, возможно, каким-то навязчивым тидиром.
Ниже мой оригинальный ответ.
Вполне возможно, что Ири прочитал статью « От моноидов к монадам» - пост, в котором Дэн Пипони (sigfpe) выводит монады из моноидов в Хаскеле, с большим обсуждением теории категорий и явным упоминанием «категории эндофункторов на Хаске ». В любом случае, любой, кто интересуется, что значит монада в категории эндофункторов, может извлечь пользу из прочтения этого вывода.
источник
:-)
.Я пришел к этому посту, чтобы лучше понять вывод печально известной цитаты из « Теории категорий Мак Лэйна для рабочего математика» .
При описании того, что является чем-то, часто одинаково полезно описать, что это не так.
Тот факт, что Mac Lane использует описание для описания монады, можно предположить, что она описывает нечто уникальное для монад. Потерпите меня. Чтобы развить более широкое понимание заявления, я полагаю, что необходимо дать понять, что он не описывает нечто уникальное для монад; утверждение одинаково описывает Аппликатив и Стрелки среди других. По той же причине у нас может быть два моноида на Int (Sum и Product), у нас может быть несколько моноидов на X в категории эндофункторов. Но есть еще больше сходства.
И Monad, и Applicative соответствуют критериям:
(например, изо дня в день
Tree a -> List b
, но в категорииTree -> List
)Tree -> List
, толькоList -> List
.В операторе используется «Категория ...» Это определяет область действия оператора. В качестве примера Категория Functor описывает область действия
f * -> g *
,Any functor -> Any functor
например,Tree * -> List *
илиTree * -> Tree *
.То, что категорическое утверждение не указывает, описывает, где что-либо и все разрешено .
В этом случае внутри функторов
* -> *
akaa -> 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
(т. е. любой параметризованный тип для любого параметризованного типа):: 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
); статическийfold
который ничего не говорит о полезной нагрузке)В Haskell важно уточнить применимость этого утверждения. Мощь и универсальность этой конструкции не имеет абсолютно никакого отношения к монаде как таковой . Другими словами, конструкция не полагается на то, что делает монаду уникальной.
При попытке выяснить, следует ли создавать код с общим контекстом для поддержки вычислений, которые зависят друг от друга, по сравнению с вычислениями, которые могут выполняться параллельно, это печально известное утверждение, как бы оно ни описывалось, не является контрастом между выбором Аппликатив, Стрелы и Монады, а скорее это описание того, насколько они одинаковы. Для данного решения утверждение является спорным.
Это часто неправильно понимают. Утверждение далее описывается
join :: m (m a) -> m a
как тензорное произведение для моноидального эндофунктора. Однако в нем не сформулировано, как в контексте этого утверждения(<*>)
также можно было бы выбрать. Это действительно пример шести / полдюжины. Логика объединения значений абсолютно одинакова; один и тот же вход генерирует одинаковый выход из каждого (в отличие от моноидов Sum и Product для Int, потому что они генерируют разные результаты при объединении Ints).Итак, резюмируем: моноид в категории эндофункторов описывает:
(<*>)
и(>>=)
оба обеспечивают одновременный доступ к двум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
источник
Ответы здесь отлично справляются с определением моноидов и монад, однако они все еще не отвечают на вопрос:
Суть вопроса, который здесь отсутствует, - это другое понятие «моноид», точнее , так называемая категоризация - понятие «моноид» в моноидальной категории. К сожалению, сама книга Мак Лэйна делает ее очень запутанной :
Главное замешательство
Почему это сбивает с толку? Потому что он не определяет, что такое «моноид в категории эндофункторов»
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
изB
, и того же тождества для любых морфизмовa,b,c
сe
замененоid_e
, тождественным морфизмомe
. Теперь полезно отметить, что в нашем интересном случае, гдеB
есть категория эндофункторовX
с естественными преобразованиями в виде морфизмов,*
композиция функторов иe
функтор тождества, все эти законы выполняются, что можно непосредственно проверить.Далее в книге дается определение «расслабленной» моноидальной категории , где законы выполняются только по модулю некоторых фиксированных естественных преобразований, удовлетворяющих так называемым отношениям когерентности. , что, однако, не важно для наших случаев категорий эндофункторов.
Моноиды в моноидальных категориях
Наконец, в разделе 3 «Моноиды» главы VII дано фактическое определение:
сделать 3 диаграммы коммутативными. Напомним, что в нашем случае это морфизмы в категории эндофункторов, которые являются естественными преобразованиями, соответствующими точно
join
иreturn
для монады. Связь становится еще более ясной, когда мы делаем композицию*
более явной, заменяя ееc * c
темc^2
, гдеc
находится наша монада.Наконец, обратите внимание, что 3 коммутативные диаграммы (в определении моноида в моноидальной категории) написаны для общих (нестрогих) моноидальных категорий, в то время как в нашем случае все естественные преобразования, возникающие как часть моноидальной категории, на самом деле являются тождествами. Это сделает диаграммы точно такими же, как в определении монады, что сделает соответствие полным.
Вывод
Таким образом, любая монада по определению является эндофунктором, следовательно, объектом в категории эндофункторов, где монады
join
иreturn
операторы удовлетворяют определению моноида в этой конкретной (строгой) моноидальной категории . Наоборот, любой моноид в моноидальной категории эндофункторов по определению является тройкой,(c, mu, nu)
состоящей из объекта и двух стрелок, например, в нашем случае естественных преобразований, удовлетворяющих тем же законам, что и монада.Наконец, обратите внимание на ключевое различие между (классическими) моноидами и более общими моноидами в моноидальных категориях. Две стрелки
mu
иnu
выше больше не являются бинарной операцией и единым целым в наборе. Вместо этого у вас есть один фиксированный endofunctorc
. Композиция функтора*
и один только функтор идентичности не обеспечивают полной структуры, необходимой для монады, несмотря на это запутанное замечание в книге.Другой подход был бы сравнить со стандартным моноиде
C
всех самостоятельных отображений множестваA
, где бинарная операция является композиция, которая может быть замечена , чтобы отобразить стандартный декартово произведениеC x C
вC
. Переходя к категоризированному моноиду, мы заменяем декартово произведениеx
на функторную композицию*
, и бинарная операция заменяется естественным преобразованиемmu
изc * c
вc
, то есть наборомjoin
операторов.для каждого объекта
T
(введите в программировании). И элементы идентичности в классических моноидах, которые могут быть идентифицированы с изображениями карт из фиксированного одноточечного множества, заменяются наборомreturn
операторовНо теперь нет больше декартовых произведений, поэтому нет пар элементов и, следовательно, нет бинарных операций.
источник