Кратко рассмотрев недавно Хаскелла, каким было бы краткое, сжатое, практическое объяснение того, что в действительности представляет собой монада?
Я обнаружил, что большинство объяснений, с которыми я столкнулся, было довольно недоступным и лишенным практических деталей.
haskell
functional-programming
monads
terminology
Peter Mortensen
источник
источник
Ответы:
Во-первых: термин монада немного бессмыслен, если вы не математик. Альтернативный термин - построитель вычислений, который немного больше описывает то, для чего они действительно полезны.
Вы просите практических примеров:
Пример 1: Понимание списка :
Это выражение возвращает двойные числа всех нечетных чисел в диапазоне от 1 до 10. Очень полезно!
Оказывается, это действительно просто синтаксический сахар для некоторых операций в монаде List. Такое же понимание списка можно записать так:
Или даже:
Пример 2: ввод / вывод :
Оба примера используют монады, построители вычислений AKA. Общая тема заключается в том, что монада цепочки операций каким-то конкретным, полезным способом. В понимании списка операции объединены в цепочку так, что если операция возвращает список, то следующие операции выполняются для каждого элемента в списке. С другой стороны, монада ввода / вывода выполняет операции последовательно, но передает «скрытую переменную», которая представляет «состояние мира», что позволяет нам писать код ввода / вывода чисто функциональным образом.
Оказывается, шаблон цепочки операций весьма полезен и используется в Haskell для множества разных вещей.
Другой пример - исключения: при использовании
Error
монады операции объединяются в цепочку так, что они выполняются последовательно, за исключением случаев, когда выдается ошибка, и в этом случае оставшаяся часть цепочки прекращается.Синтаксис осмысления списка и нотация do являются синтаксическим сахаром для операций цепочки с использованием
>>=
оператора. Монада - это просто тип, который поддерживает>>=
оператор.Пример 3: парсер
Это очень простой парсер, который анализирует либо строку в кавычках, либо число:
Операции
char
,digit
и т.д. довольно просты. Они либо совпадают, либо не совпадают. Волшебство - это монада, которая управляет потоком управления: операции выполняются последовательно до тех пор, пока совпадение не завершится, и в этом случае монада возвращается к последнему<|>
и пробует следующую опцию. Опять же, способ объединения операций с некоторой дополнительной полезной семантикой.Пример 4: Асинхронное программирование
Приведенные выше примеры есть в Haskell, но, оказывается, F # также поддерживает монады. Этот пример украден у Дона Сайма :
Этот метод выбирает веб-страницу. Изюминкой является использование
GetResponseAsync
- он на самом деле ожидает ответа в отдельном потоке, в то время как основной поток возвращается из функции. Последние три строки выполняются в порожденном потоке, когда ответ получен.В большинстве других языков вам придется явно создать отдельную функцию для строк, которые обрабатывают ответ.
async
Монада в состоянии «раскол» блок на своем и отложить исполнение второй половины. (async {}
Синтаксис указывает, что поток управления в блоке определяетсяasync
монадой.)Как они работают
Так как же монада может делать все эти причудливые вещи управления потоком? Что действительно происходит в do-блоке (или вычислительном выражении, как они называются в F #), так это то, что каждая операция (в основном каждая строка) заключена в отдельную анонимную функцию. Эти функции затем объединяются с помощью
bind
оператора (пишется>>=
на Haskell). Посколькуbind
операция объединяет функции, она может выполнять их так, как считает нужным: последовательно, несколько раз, в обратном порядке, отбрасывать некоторые, выполнять некоторые в отдельном потоке, когда чувствует себя так, и так далее.В качестве примера, это расширенная версия IO-кода из примера 2:
Это уродливее, но также более очевидно, что на самом деле происходит.
>>=
Оператор волшебный ингредиент: Он принимает значение (на левой стороне) и комбинирует ее с функцией (на правой стороне), чтобы произвести новое значение. Это новое значение затем берется следующим>>=
оператором и снова объединяется с функцией для создания нового значения.>>=
можно рассматривать как мини-оценщик.Обратите внимание, что
>>=
он перегружен для разных типов, поэтому каждая монада имеет свою реализацию>>=
. (Все операции в цепочке должны быть одного типа, хотя>>=
оператор не будет работать.)Простейшая возможная реализация
>>=
просто берет значение слева и применяет его к функции справа и возвращает результат, но, как уже говорилось ранее, весь шаблон полезен, когда в реализации монады происходит что-то дополнительное.>>=
,Существует некоторая дополнительная хитрость в том, как значения передаются от одной операции к другой, но это требует более глубокого объяснения системы типов Haskell.
Подводя итоги
В терминах Haskell монада - это параметризованный тип, который является экземпляром класса типов Monad, который определяет
>>=
наряду с несколькими другими операторами. С точки зрения непрофессионала, монада - это просто тип, для которого определена>>=
операция.Сам по себе
>>=
это всего лишь громоздкий способ объединения функций, но при наличии нотации, скрывающей «слесарное дело», монадические операции оказываются очень хорошей и полезной абстракцией, полезной во многих местах в языке и полезной для создания собственных мини-языков на языке.Почему монады трудны?
Для многих изучающих Хаскель монады являются препятствием, которое они наносят, как кирпичная стена. Дело не в том, что сами монады являются сложными, а в том, что реализация опирается на многие другие расширенные функции Haskell, такие как параметризованные типы, классы типов и так далее. Проблема заключается в том, что ввод / вывод Haskell основан на монадах, и ввод / вывод, вероятно, является одной из первых вещей, которую вы хотите понять при изучении нового языка - в конце концов, создавать программы, которые не производят никаких программ, не очень интересно вывод. У меня нет немедленного решения этой проблемы «курица-яйцо», за исключением обработки ввода-вывода как «волшебство происходит здесь», пока у вас не будет достаточно опыта работы с другими частями языка. Сожалею.
Отличный блог о монадах: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
источник
Объяснение "что такое монада" немного похоже на выражение "что такое число?" Мы используем номера все время. Но представьте, что вы встретили человека, который ничего не знал о числах. Как, черт возьми , вы объясните, что это за цифры? И как бы вы начали описывать, почему это может быть полезно?
Что такое монада? Краткий ответ: это особый способ объединения операций.
По сути, вы пишете шаги выполнения и связываете их вместе с помощью «функции связывания». (В Haskell он называется
>>=
.) Вы можете написать вызовы оператора bind самостоятельно или использовать синтаксический сахар, который заставляет компилятор вставлять эти вызовы функций для вас. Но в любом случае каждый шаг отделяется вызовом этой функции связывания.Таким образом, функция связывания похожа на точку с запятой; он разделяет шаги в процессе. Задача функции связывания состоит в том, чтобы взять выходные данные с предыдущего шага и передать их на следующий шаг.
Это звучит не слишком сложно, верно? Но есть более чем один вид монады. Почему? Как?
Ну, функция связывания может просто взять результат с одного шага и передать его на следующий шаг. Но если это "все", что делает монада ... это на самом деле не очень полезно. И это важно понимать: каждая полезная монада делает что-то еще, кроме того, что она монада. Каждая полезная монада обладает «особой силой», которая делает ее уникальной.
(Монада, которая не делает ничего особенного, называется «монадой идентичности». Скорее, как функция идентичности, это звучит как совершенно бессмысленная вещь, но оказывается, что это не так… Но это другая история ™.)
По сути, каждая монада имеет свою собственную реализацию функции связывания. И вы можете написать функцию связывания так, чтобы она выполняла определенные действия между этапами выполнения. Например:
Если каждый шаг возвращает индикатор успеха / неудачи, bind может выполнить следующий шаг, только если предыдущий был успешным. Таким образом, неудачный шаг прерывает всю последовательность «автоматически», без каких-либо условных проверок с вашей стороны. ( Отказ Монада .)
Расширяя эту идею, вы можете реализовать «исключения». ( Монада ошибок или монада исключений .) Поскольку вы определяете их сами, а не как языковую функцию, вы можете определить, как они работают. (Например, может быть, вы хотите игнорировать первые два исключения и прерывать только когда третьего исключения.)
Вы можете заставить каждый шаг возвращать несколько результатов , и иметь над ними цикл функции связывания, передавая каждый следующий шаг для вас. Таким образом, вам не нужно постоянно писать циклы повсеместно при работе с несколькими результатами. Функция привязки «автоматически» сделает все это за вас. ( Список Монад .)
Помимо передачи «результата» от одного шага к другому, вы можете также использовать функцию связывания для передачи дополнительных данных . Эти данные теперь не отображаются в вашем исходном коде, но вы все равно можете получить к ним доступ из любого места без необходимости вручную передавать их каждой функции. ( Читатель Монада .)
Вы можете сделать так, чтобы «дополнительные данные» можно было заменить. Это позволяет вам моделировать деструктивные обновления , фактически не делая деструктивных обновлений. ( Государственная Монада и ее двоюродный брат Писатель Монада .)
Потому что вы только имитируя деструктивные обновления, вы можете тривиальным делать то , что было бы невозможно с реальными деструктивными обновлениями. Например, вы можете отменить последнее обновление или вернуться к более старой версии .
Вы можете создать монаду, в которой вычисления могут быть приостановлены , так что вы можете приостановить свою программу, войти и поработать с внутренними данными состояния, а затем возобновить их.
Вы можете реализовать «продолжения» как монаду. Это позволяет вам ломать умы людей!
Все это и многое другое возможно с монадами. Конечно, все это также вполне возможно без монад. С помощью монад просто проще .
источник
На самом деле, вопреки общему пониманию монад, они не имеют ничего общего с государством. Монады - это просто способ обернуть вещи и предоставить методы для выполнения операций над обернутыми вещами, не разворачивая их.
Например, вы можете создать тип для переноса другого в Haskell:
Чтобы обернуть вещи мы определяем
Для выполнения операций без развертывания, скажем, у вас есть функция
f :: a -> b
, вы можете сделать это, чтобы поднять эту функцию, чтобы воздействовать на упакованные значения:Вот и все, что нужно понять. Тем не менее, оказывается, что есть более общая функция , чтобы сделать этот подъем , который
bind
:bind
может сделать немного большеfmap
, но не наоборот. На самом деле,fmap
можно определить только с точки зренияbind
иreturn
. Итак, при определении монады .. вы даете ее тип (здесь это былоWrapped a
), а затем говорите, как ееreturn
иbind
рабочие операции.Круто то, что это такой общий паттерн, который всплывает повсюду, инкапсулируя состояние в чистом виде - только один из них.
Для хорошей статьи о том, как монады могут использоваться для введения функциональных зависимостей и, таким образом, управления порядком оценки, как это используется в монаде IO Haskell, ознакомьтесь с IO Inside .
Что касается понимания монад, не беспокойтесь об этом. Читайте о них то, что вам интересно, и не беспокойтесь, если не поймете сразу. Тогда просто погрузиться на таком языке, как Хаскелл. Монады - это одна из тех вещей, где понимание проникает в ваш мозг практикой, однажды вы вдруг понимаете, что понимаете их.
источник
Но вы могли бы изобрести монады!
источник
Монада - это тип данных, который имеет две операции:
>>=
(акаbind
) иreturn
(акаunit
).return
принимает произвольное значение и создает экземпляр монады вместе с ним.>>=
берет экземпляр монады и отображает над ним функцию. (Вы уже можете видеть, что монада - это странный тип данных, поскольку в большинстве языков программирования вы не можете написать функцию, которая принимает произвольное значение и создает из него тип. Монады используют своего рода параметрический полиморфизм .)В нотации Haskell написан интерфейс монады
Предполагается, что эти операции подчиняются определенным «законам», но это не очень важно: «законы» просто кодифицируют способ, которым должны вести себя разумные реализации операций (в основном это
>>=
иreturn
должно согласовывать, как значения преобразуются в экземпляры монад и который>>=
ассоциативно).Монады - это не только состояние и ввод / вывод: они абстрагируют общую схему вычислений, которая включает в себя работу с состоянием, вводом / выводом, исключениями и недетерминизмом. Вероятно, простейшие для понимания монады - это списки и типы опций:
где
[]
и:
являются списочными конструкторами,++
оператором конкатенации, иJust
иNothing
являютсяMaybe
конструкторы. Обе эти монады инкапсулируют общие и полезные шаблоны вычислений для их соответствующих типов данных (обратите внимание, что ни один из них не имеет ничего общего с побочными эффектами или вводом / выводом).Вы действительно должны поиграть в написание некоторого нетривиального кода на Haskell, чтобы оценить, что такое монады и почему они полезны.
источник
Сначала вы должны понять, что такое функтор. Перед этим разберитесь с функциями высшего порядка.
Функция высшего порядка - это просто функция, которая принимает функцию в качестве аргумента.
Функтор является любой тип конструкции ,
T
для которой существует функция высшего порядка, назовем егоmap
, который преобразовывает функцию типаa -> b
( с учетом любых двух типов ,a
аb
) в функциюT a -> T b
. Этаmap
функция также должна подчиняться законам идентичности и композиции, чтобы следующие выражения возвращали true для всехp
иq
(нотация Haskell):Например, конструктор типа называется
List
функтором, если он снабжен функцией типа,(a -> b) -> List a -> List b
которая подчиняется законам выше. Единственная практическая реализация очевидна. РезультирующаяList a -> List b
функция выполняет итерацию по заданному списку, вызывая(a -> b)
функцию для каждого элемента, и возвращает список результатов.Монада по существу только функтор
T
с двумя дополнительными методами,join
, типаT (T a) -> T a
, иunit
(иногда называютreturn
,fork
илиpure
) типаa -> T a
. Для списков в Haskell:Почему это полезно? Потому что вы можете, например,
map
над списком с функцией, которая возвращает список.Join
берет полученный список списков и объединяет их.List
это монада, потому что это возможно.Вы можете написать функцию, которая делает
map
, тоjoin
. Эта функция называетсяbind
, илиflatMap
, или(>>=)
, или(=<<)
. Это обычно, как экземпляр монады дается в Haskell.Монада должна удовлетворять определенным законам, а именно, она
join
должна быть ассоциативной. Это означает, что если у вас есть значениеx
типа,[[[a]]]
тоjoin (join x)
должно равнятьсяjoin (map join x)
. Иpure
должно быть тож дляjoin
такогоjoin (pure x) == x
.источник
[Отказ от ответственности: я все еще пытаюсь полностью поглощать монады. Вот что я понял до сих пор. Если это не так, надеюсь, кто-то знающий позвонит мне на ковер.]
Арнар написал:
Это именно так. Идея звучит так:
Вы берете какую-то ценность и оборачиваете ее дополнительной информацией. Точно так же, как значение определенного типа (например, целое число или строка), так и дополнительная информация имеет определенный вид.
Например, эта дополнительная информация может быть
Maybe
илиIO
.Затем у вас есть несколько операторов, которые позволяют вам работать с обернутыми данными, сохраняя при этом эту дополнительную информацию. Эти операторы используют дополнительную информацию, чтобы решить, как изменить поведение операции для переносимого значения.
Например,
Maybe Int
может бытьJust Int
илиNothing
. Теперь, если вы добавите aMaybe Int
к aMaybe Int
, оператор проверит, находятся ли ониJust Int
внутри s, и, если это так, развернетInt
s, передаст им оператор сложения, перевернет полученный результатInt
в новыйJust Int
(который является действительнымMaybe Int
) и, таким образом, вернутьMaybe Int
. Но если один из них былNothing
внутри, этот оператор сразу же вернетсяNothing
, что снова является действительнымMaybe Int
. Таким образом, вы можете притвориться, что вашиMaybe Int
числа просто нормальные, и выполнять с ними регулярные вычисления. Если бы вы получилиNothing
, ваши уравнения все равно будут давать правильный результат - без необходимости мусорить проверкиNothing
везде .Но пример как раз то, что происходит
Maybe
. Если бы дополнительная информация была aIO
, тоIO
вместо этого вызывался бы этот специальный оператор, определенный для s, и он мог сделать что-то совершенно другое перед выполнением сложения. (Хорошо, добавление двухIO Int
s вместе, вероятно, бессмысленно - я еще не уверен.) (Кроме того, если вы обратили внимание наMaybe
пример, вы заметили, что «оборачивать значение дополнительными вещами» не всегда правильно. Но это сложно быть точным, правильным и точным, не будучи непостижимым.)По сути, «монада» примерно означает «шаблон» . Но вместо книги, полной неформально объясненных и конкретно названных шаблонов, у вас теперь есть языковая конструкция - синтаксис и все - что позволяет вам объявлять новые шаблоны как вещи в вашей программе . (Неточность здесь заключается в том, что все паттерны должны следовать определенной форме, поэтому монада не так универсальна, как паттерн. Но я думаю, что это самый близкий термин, который большинство людей знают и понимают.)
И именно поэтому люди находят монады настолько запутанными: потому что они - такая общая концепция. Спрашивать, что делает что-то монадой, также неопределенно, как спрашивать, что делает что-то образцом.
Но подумайте о последствиях наличия синтаксической поддержки в языке для идеи шаблона: вместо того, чтобы читать книгу « Банда четырех» и запоминать конструкцию конкретного шаблона, вы просто пишете код, который реализует этот шаблон в агностике, общий способ один раз, и тогда вы сделали! Затем вы можете повторно использовать этот шаблон, например Visitor, Strategy, Façade или любой другой, просто украсив им операции в вашем коде, без необходимости повторной реализации его снова и снова!
Вот почему люди, которые понимают монады, находят их такими полезными : это не какая-то концепция башни из слоновой кости, которую интеллектуальные снобы гордятся пониманием (хорошо, это тоже, конечно, хихикать), но на самом деле делает код проще.
источник
M (M a) -> M a
. Тот факт, что вы можете превратить это в один из типов,M a -> (a -> M b) -> M b
делает их полезными.После долгих попыток, я думаю, я наконец понял монаду. Перечитав мою собственную длинную критику ответа с подавляющим большинством голосов, я предложу это объяснение.
Есть три вопроса, на которые нужно ответить, чтобы понять монады:
Как я отмечал в моих первоначальных комментариях, слишком много объяснений монады попадают в вопрос № 3, прежде чем он действительно адекватно охватывает вопрос 2 или вопрос 1.
Зачем вам нужна монада?
Чистые функциональные языки, такие как Haskell, отличаются от императивных языков, таких как C или Java, тем, что чисто функциональные программы не обязательно выполняются в определенном порядке, по одному шагу за раз. Программа на Haskell больше похожа на математическую функцию, в которой вы можете решить «уравнение» в любом количестве возможных порядков. Это дает ряд преимуществ, среди которых заключается в том, что исключает возможность ошибок определенного рода, особенно тех, которые связаны с такими вещами, как «состояние».
Однако есть некоторые проблемы, которые не так просто решить с помощью этого стиля программирования. Некоторые вещи, такие как консольное программирование и файловый ввод-вывод, должны происходить в определенном порядке или поддерживать состояние. Одним из способов решения этой проблемы является создание вида объекта, который представляет состояние вычисления, и ряда функций, которые принимают объект состояния в качестве входных данных и возвращают новый измененный объект состояния.
Итак, давайте создадим гипотетическое значение «state», которое представляет состояние экрана консоли. как именно это значение создается, не важно, но допустим, что это массив символов ascii длины байта, который представляет то, что в данный момент видно на экране, и массив, который представляет последнюю строку ввода, введенную пользователем, в псевдокоде. Мы определили некоторые функции, которые принимают состояние консоли, изменяют его и возвращают новое состояние консоли.
Таким образом, чтобы выполнять консольное программирование, но чисто функциональным способом, вам нужно будет вкладывать много вызовов функций друг в друга.
Программирование таким образом сохраняет «чистый» функциональный стиль, заставляя изменения в консоли происходить в определенном порядке. Но мы, вероятно, захотим сделать больше, чем просто несколько операций за один раз, как в приведенном выше примере. Вложенные функции таким образом начнут становиться неловкими. То, что мы хотим, это код, который по сути делает то же самое, что и выше, но написан немного больше так:
Это действительно был бы более удобный способ написать это. Как мы это делаем, хотя?
Что такое монада?
Как только у вас есть тип (такой как
consolestate
), который вы определяете вместе с набором функций, разработанных специально для работы с этим типом, вы можете превратить весь пакет этих вещей в «монаду», определив оператор типа:
(bind), который автоматически возвращает возвращаемые значения слева, в параметры функции справа, иlift
оператор, который превращает нормальные функции, в функции, которые работают с этим специфическим видом оператора связывания.Как реализована монада?
Посмотрите другие ответы, которые, кажется, совершенно свободно прыгать в детали этого.
источник
Ответив на этот вопрос несколько лет назад, я считаю, что могу улучшить и упростить этот ответ с помощью ...
Монада - это метод композиции функций, который экстернализует обработку некоторых входных сценариев с использованием функции компоновки
bind
для предварительной обработки ввода во время композиции.В обычной композиции функция
compose (>>)
используется для последовательного применения составной функции к результату ее предшественника. Важно отметить, что составляемая функция необходима для обработки всех сценариев ее ввода.(x -> y) >> (y -> z)
Эту конструкцию можно улучшить, реструктурировав входные данные, чтобы легче было запрашивать соответствующие состояния. Таким образом, вместо того, чтобы просто
y
значение может статьMb
таким, как, например,(is_OK, b)
еслиy
включено понятие валидности.Так , например, когда вход только возможно , число, вместо того , чтобы возвращать строку , которая может содержать покорно содержать ряд или нет, вы могли бы перестроить тип в
bool
указывающем на наличие действительного числа и число в кортеже , такие как,bool * float
. Составленным функциям теперь больше не нужно анализировать входную строку, чтобы определить, существует ли число, а можно просто проверитьbool
часть кортежа.(Ma -> Mb) >> (Mb -> Mc)
Здесь опять-таки составление происходит естественным образом,
compose
и поэтому каждая функция должна обрабатывать все сценарии своего ввода по отдельности, хотя теперь это сделать намного проще.Однако, что если бы мы могли вывести усилия на допрос в те времена, когда обработка сценария является рутинной. Например, что если наша программа не делает ничего , если вход не в порядке , как в том, когда
is_OK
этоfalse
. Если бы это было сделано, то составным функциям не нужно было бы самим обрабатывать этот сценарий, что значительно упрощало бы их код и приводило к другому уровню повторного использования.Для достижения этой экстернализации мы могли бы использовать функцию,
bind (>>=)
, для выполненияcomposition
вместоcompose
. Таким образом, вместо простой передачи значений из выходных данных одной функции на входные данные другойBind
будет проверятьM
частьMa
и решать, применять ли составную функцию к и и какa
. Конечно, функцияbind
будет определена специально для нашего конкретногоM
случая, чтобы иметь возможность проверять ее структуру и выполнять любые приложения, которые мы хотим. Тем не менее, оноa
может быть чем угодно, посколькуbind
просто пропускаетa
неинспектированное составной функции, когда оно определяет необходимое применение. Кроме того, сами составные функции больше не должны иметь дело сM
часть структуры ввода либо, упрощая их. Следовательно ...(a -> Mb) >>= (b -> Mc)
или более краткоMb >>= (b -> Mc)
Короче говоря, монада экстернализуется и, таким образом, обеспечивает стандартное поведение при обработке определенных сценариев ввода, как только ввод становится разработанным для их достаточного раскрытия. Этот дизайн представляет собой
shell and content
модель, в которой оболочка содержит данные, относящиеся к применению составной функции, и запрашивается и остается доступной только для этойbind
функции.Следовательно, монада это три вещи:
M
оболочка для проведения монады соответствующей информации,bind
функции реализованы , чтобы использовать эту информацию оболочки в ее применении в составе функций к стоимости контента (ы) , которые он находит внутри оболочки, иa -> Mb
производящие результаты, которые включают монадические данные управления.Вообще говоря, входные данные для функции гораздо более строгие, чем ее выходные данные, которые могут включать в себя такие вещи, как условия ошибки; следовательно,
Mb
структура результата, как правило, очень полезна. Например, оператор деления не возвращает число, когда делитель0
.Кроме того,
monad
s может включать в себя функции переноса, которые переносят значенияa
в монадический типMa
, и общие функцииa -> b
в монадические функции,a -> Mb
заключая их результаты после применения. Конечно,bind
такие функции переноса являются специфическими дляM
. Пример:Конструкция
bind
функции предполагает неизменные структуры данных и чистые функции, другие вещи становятся сложными и гарантии не могут быть сделаны. Таким образом, существуют монадические законы:Данный...
Затем...
Associativity
означает, чтоbind
сохраняет порядок оценки независимо от того, когдаbind
применяется. То есть, в определенииAssociativity
выше, в силу ранней оценки из скобкиbinding
изf
иg
приведет лишь к функции , которая ожидаетMa
в целях завершенияbind
. Следовательно, оценкаMa
должна быть определена до того, как ее значение может быть применено,f
и этот результат, в свою очередь, будет применен кg
.источник
Монада, по сути, является формой «оператора типа». Это сделает три вещи. Сначала он «обернет» (или иным образом преобразует) значение одного типа в другой тип (обычно называемый «монадическим типом»). Во-вторых, он сделает все операции (или функции) доступными для базового типа доступными для монадического типа. Наконец, он обеспечит поддержку для объединения себя с другой монадой для создания составной монады.
«Возможно, монада» по существу эквивалентна «обнуляемым типам» в Visual Basic / C #. Он принимает необнуляемый тип «T» и преобразует его в «Nullable <T>», а затем определяет, что означают все двоичные операторы для Nullable <T>.
Побочные эффекты представлены одинаково. Создается структура, которая содержит описания побочных эффектов наряду с возвращаемым значением функции. «Поднятые» операции затем копируют вокруг побочных эффектов, когда значения передаются между функциями.
Они называются «монадами», а не более простым для понимания именем «операторов типа» по нескольким причинам:
источник
(См. Также ответы в разделе Что такое монада? )
Хорошей мотивацией для Монад является sigfpe (Дэн Пипони), « Вы могли бы изобрести монады» ! (А может быть, у вас уже есть) . Существует множество других учебных пособий по монадам, многие из которых ошибочно пытаются объяснить монады «простыми терминами», используя различные аналогии: это ошибка учебного пособия по монадам. ; избежать их.
Как говорит DR MacIver в Расскажите нам, почему ваш язык отстой :
Вы говорите, что понимаете монаду Может быть? Хорошо, ты уже в пути. Просто начните использовать другие монады, и рано или поздно вы поймете, что такое монады в целом.
[Если вы ориентированы на математику, вы можете игнорировать десятки учебных пособий и выучить определение или следовать лекциям по теории категорий. :) Основная часть определения состоит в том, что Monad M включает в себя «конструктор типов», который определяет для каждого существующий тип "T", новый тип "MT" и некоторые способы перехода назад и вперед между "обычными" типами и типами "M".]
Кроме того, как ни удивительно, одним из лучших введений в монады на самом деле является одна из ранних научных статей, представляющих монады, монады Филиппа Уодлера для функционального программирования . На самом деле в нем есть практические, нетривиальные мотивирующие примеры, в отличие от многих искусственных учебников.
источник
Монады должны управлять потоком, каким абстрактные типы данных являются для данных.
Другими словами, многим разработчикам нравится идея наборов, списков, словарей (или хешей, или карт) и деревьев. Внутри этих типов данных существует много особых случаев (например, InsertionOrderPreservingIdentityHashMap).
Однако, сталкиваясь с программным потоком, многие разработчики не сталкивались с гораздо большим количеством конструкций, чем if, switch / case, do, while, goto (grr) и (возможно) замыкания.
Итак, монада - это просто конструкция потока управления. Лучшей фразой для замены монады будет «тип управления».
Таким образом, монада имеет слоты для управляющей логики, или операторов, или функций - эквивалент в структурах данных будет означать, что некоторые структуры данных позволяют вам добавлять данные и удалять их.
Например, монада "если":
в самом простом случае имеет два слота - предложение и блок.
if
Монада, как правило , построены , чтобы оценить результат статьи, и если не ложно, оценивать блок. Многие разработчики не знакомы с монадами, когда они изучают «если», и просто нет необходимости понимать монады, чтобы писать эффективную логику.Монады могут становиться более сложными, так же как структуры данных могут становиться более сложными, но существует много широких категорий монад, которые могут иметь сходную семантику, но отличающиеся реализации и синтаксис.
Конечно, таким же образом, что структуры данных могут быть итерированы или пройдены по монадам, могут быть оценены.
Компиляторы могут иметь или не иметь поддержку пользовательских монад. Хаскелл, конечно, делает. Ioke имеет некоторые аналогичные возможности, хотя термин монада не используется в языке.
источник
Мой любимый учебник Monad:
http://www.haskell.org/haskellwiki/All_About_Monads
(из 170 000 просмотров в поиске Google "учебник монад"!)
@Stu: смысл монад в том, чтобы позволить вам добавить (обычно) последовательную семантику в чистый код; Вы даже можете создавать монады (используя Monad Transformers) и получать более интересную и сложную комбинированную семантику, например, например, анализ с обработкой ошибок, общим состоянием и журналированием. Все это возможно в чистом коде, монады просто позволяют абстрагировать его и повторно использовать в модульных библиотеках (всегда хороших в программировании), а также предоставляют удобный синтаксис, чтобы сделать его обязательным.
На Haskell уже есть перегрузка операторов [1]: он использует классы типов так же, как можно было бы использовать интерфейсы в Java или C #, но Haskell просто позволяет также использовать не алфавитно-цифровые токены, такие как + && и>, в качестве идентификаторов инфиксов. Это только перегрузка оператора, если вы имеете в виду «перегрузка точки с запятой» [2]. Звучит как чёрная магия, и возникает проблема с «перегрузкой точки с запятой» (представьте, что предприимчивые хакеры Perl узнают об этой идее), но дело в том, что без монад нет точки с запятой, поскольку чисто функциональный код не требует или не допускает явной последовательности.
Все это звучит намного сложнее, чем нужно. Статья sigfpe довольно крутая, но для объяснения она использует Haskell, что не решает проблему курицы и яйца, заключающуюся в том, чтобы понять Haskell, чтобы обмануть Monads, и понять Monads, чтобы обмануть Haskell.
[1] Это отдельная проблема от монад, но монады используют функцию перегрузки операторов Haskell.
[2] Это также упрощение, так как оператор для связывания монадических действий: >> = (произносится «связать»), но есть синтаксический сахар («до»), который позволяет вам использовать фигурные скобки и точки с запятой и / или отступы и символы новой строки.
источник
В последнее время я думаю о Монаде иначе. Я думал о них как об абстрактном порядке выполнения математическим способом, который делает возможными новые виды полиморфизма.
Если вы используете императивный язык и пишете некоторые выражения по порядку, код ВСЕГДА выполняется именно в таком порядке.
И в простом случае, когда вы используете монаду, вы чувствуете то же самое - вы определяете список выражений, которые встречаются по порядку. Кроме того, в зависимости от того, какую монаду вы используете, ваш код может выполняться по порядку (как в монаде IO), параллельно по нескольким элементам одновременно (как в монаде списка), он может останавливаться на полпути (как в монаде Maybe) , он может приостановиться на полпути, чтобы быть возобновленным позже (как в монаде Возобновления), он может перемотаться и начаться с начала (как в монаде Транзакции), или он может перемотаться на полпути, чтобы попробовать другие варианты (как в монаде Логики) ,
А поскольку монады полиморфны, можно запускать один и тот же код в разных монадах в зависимости от ваших потребностей.
Кроме того, в некоторых случаях возможно объединение монад (вместе с преобразователями монад) для одновременного получения нескольких функций.
источник
Я все еще новичок в монадах, но я подумал, что поделюсь ссылкой, которую я нашел и которую очень приятно читать (С ФОТОГРАФИЯМИ!): Http://www.matusiak.eu/numerodix/blog/2012/3/11/ монады для обывателя / (без принадлежности)
По сути, теплая и нечеткая концепция, которую я получил из этой статьи, заключалась в том, что монады - это в основном адаптеры, которые позволяют разнородным функциям работать комбинируемым образом, т.е. иметь возможность объединять несколько функций, смешивать и сопоставлять их, не беспокоясь о непоследовательном возврате типы и тому подобное. Таким образом, функция BIND отвечает за хранение яблок с яблоками и апельсинов с апельсинами, когда мы пытаемся сделать эти адаптеры. А функция LIFT отвечает за использование функций «нижнего уровня» и «модернизацию» их для работы с функциями BIND, а также для их компоновки.
Надеюсь, я понял это правильно, и, что более важно, надеюсь, что статья имеет правильное представление о монадах. Эта статья помогла мне разжечь аппетит к изучению монад.
источник
В дополнение к превосходным ответам, приведенным выше, позвольте мне предложить вам ссылку на следующую статью (Патрик Томсон), в которой объясняются монады, связывая концепцию с библиотекой JavaScript jQuery (и ее способ использования «цепочки методов» для манипулирования DOM). : jQuery - это монада
Сама документация jQuery не ссылается на термин «монада», но говорит о «шаблоне компоновщика», который, вероятно, более знаком. Это не меняет того факта, что у вас есть настоящая монада, может быть, даже не осознавая этого.
источник
Монады - это не метафоры , а практически полезная абстракция, возникающая из общего паттерна, как объясняет Даниэль Спивак.
источник
Монада - это способ объединения вычислений, которые имеют общий контекст. Это похоже на построение сети труб. При построении сети данные по ней не передаются. Но когда я закончил объединять все биты вместе с 'bind' и 'return', тогда я вызываю что-то вроде,
runMyMonad monad data
и данные проходят через каналы.источник
На практике monad - это пользовательская реализация оператора композиции функций, которая заботится о побочных эффектах и несовместимых входных и возвращаемых значениях (для формирования цепочки).
источник
Если я правильно понял, IEnumerable является производным от монад. Интересно, может ли это быть интересным углом зрения для тех из нас, кто живет в мире C #?
Для чего это стоит, вот несколько ссылок на учебники, которые мне помогли (и нет, я до сих пор не понял, что такое монады).
источник
Две вещи, которые помогли мне лучше всего, узнав об этом, были:
Глава 8, «Функциональные парсеры», из книги Грэма Хаттона « Программирование на Хаскеле» . На самом деле это вообще не касается монад, но если вы сможете проработать главу и по-настоящему понять все в ней, особенно то, как оценивается последовательность операций связывания, вы поймете внутренности монад. Ожидайте, что это займет несколько попыток.
Учебник Все о монадах . Это дает несколько хороших примеров их использования, и я должен сказать, что аналогия в Appendex я работал для меня.
источник
Monoid, по-видимому, гарантирует, что все операции, определенные для Monoid и поддерживаемого типа, всегда будут возвращать поддерживаемый тип внутри Monoid. Например, любое число + любое число = число, без ошибок.
Принимая во внимание, что деление принимает два дробных числа и возвращает дробное число, которое определило деление на ноль как бесконечность в haskell, почему-то (что иногда является дробным некоторым образом) ...
В любом случае кажется, что Monads - это просто способ гарантировать, что ваша цепочка операций ведет себя предсказуемо, а функция, которая претендует на значение Num -> Num, составленная с другой функцией Num-> Num, вызываемой с помощью x, не скажем, стрелять ракетами.
С другой стороны, если у нас есть функция, которая запускает ракеты, мы можем объединить ее с другими функциями, которые также запускают ракеты, потому что наше намерение ясно - мы хотим запустить ракеты - но она не будет пытаться печать "Hello World" по какой-то странной причине.
В Haskell main имеет тип IO () или IO [()], это странное распределение, и я не буду его обсуждать, но вот что я думаю:
Если у меня есть main, я хочу, чтобы он выполнял цепочку действий, потому что я запускаю программу, чтобы произвести эффект - обычно через IO. Таким образом, я могу объединить операции ввода-вывода в главном порядке, чтобы выполнить ввод-вывод, ничего больше.
Если я пытаюсь сделать что-то, что не «возвращает IO», программа будет жаловаться, что цепочка не течет, или, по сути, «как это связано с тем, что мы пытаемся сделать - действие IO», это, кажется, заставляет программист, чтобы держать их ход мыслей, не отклоняясь и не думая об увольнении ракет, создавая алгоритмы для сортировки - что не происходит.
По сути, Monads - это подсказка для компилятора: «эй, вы знаете эту функцию, которая возвращает число здесь, она на самом деле не всегда работает, иногда она может генерировать число, а иногда вообще ничего, просто держите это в разум". Зная это, если вы пытаетесь утвердить монадическое действие, монадическое действие может действовать как исключение времени компиляции, говоря «эй, это на самом деле не число, это МОЖЕТ быть числом, но вы не можете предположить это, сделать что-то чтобы гарантировать, что поток является приемлемым. " что предотвращает непредсказуемое поведение программы - в значительной степени.
Похоже, монады не о чистоте и не контроле, а о сохранении идентичности категории, для которой все поведение предсказуемо и определено или не компилируется. Вы не можете ничего делать, когда от вас ожидают что-то сделать, и вы не можете делать что-то, если от вас ожидают ничего (видимого).
Самая большая причина, по которой я мог придумать Monads, - это посмотреть на процедурный / ООП-код, и вы заметите, что вы не знаете, где начинается или заканчивается программа, все, что вы видите, это много прыжков и много математики , магия и ракеты. Вы не сможете поддерживать его, и, если сможете, вы потратите довольно много времени на то, чтобы сосредоточиться на всей программе, прежде чем сможете понять какую-либо ее часть, потому что модульность в этом контексте основана на взаимозависимых «разделах». кода, где код оптимизирован, чтобы быть как можно более связанными для обеспечения эффективности / взаимосвязи. Монады очень конкретны и хорошо определены по определению и обеспечивают возможность анализа потока программы и выделения частей, которые трудно анализировать - так как они сами являются монадами. Монада кажется " или уничтожить вселенную, или даже исказить время - мы понятия не имеем и не имеем никаких гарантий, что это то, что есть. Монада ГАРАНТИРУЕТ, ЧТО ЭТО ТАКОЕ. что очень сильно. или уничтожить вселенную, или даже исказить время - мы понятия не имеем и не имеем никаких гарантий, что это то, что есть. Монада ГАРАНТИРУЕТ, ЧТО ЭТО ТАКОЕ. что очень сильно.
Все вещи в «реальном мире» кажутся монадами в том смысле, что они связаны определенными наблюдаемыми законами, предотвращающими путаницу. Это не означает, что мы должны имитировать все операции этого объекта для создания классов, вместо этого мы можем просто сказать «квадрат есть квадрат», ничего, кроме квадрата, даже не прямоугольника и не круга, и «квадрат имеет площадь длины одного из его существующих измерений, умноженных на себя. Независимо от того, какой у вас квадрат, если это квадрат в 2D-пространстве, его площадь абсолютно не может быть ничем, кроме его длины в квадрате, это почти тривиально доказать. Это очень сильно, потому что нам не нужно делать утверждения, чтобы убедиться, что наш мир такой, какой он есть, мы просто используем значение реальности, чтобы не допустить срыва наших программ.
Я гарантированно ошибаюсь, но я думаю, что это может кому-то помочь, так что, надеюсь, это кому-то поможет.
источник
В контексте Scala вы найдете следующее определение. По сути, flatMap (или связывание) является «ассоциативным» и существует идентичность.
Например
ПРИМЕЧАНИЕ. Строго говоря, определение монады в функциональном программировании не совпадает с определением монады в теории категорий , которое определяется поочередно
map
иflatten
. Хотя они являются своего рода эквивалентом при определенных отображениях. Эти презентации очень хороши: http://www.slideshare.net/samthemonad/monad-presentation-scala-as-a-categoryисточник
Этот ответ начинается с мотивирующего примера, проходит через пример, выводит пример монады и формально определяет «монаду».
Рассмотрим эти три функции в псевдокоде:
f
принимает упорядоченную пару формы<x, messages>
и возвращает упорядоченную пару. Первый элемент остается нетронутым и добавляется"called f. "
ко второму элементу. То же самое сg
.Вы можете составить эти функции и получить исходное значение вместе со строкой, показывающей, в каком порядке были вызваны функции:
Вам не нравится тот факт , что
f
иg
несут ответственность за добавление своих собственных сообщений журнала предыдущей информации о регистрации. (Просто представьте ради аргумента, что вместо добавления строк,f
иg
должны выполнять сложную логику на второй элемент пары Было бы боль , чтобы повторить , что сложная логика в двух -. Или более. - различные функции)Вы предпочитаете писать более простые функции:
Но посмотрите, что происходит, когда вы их составляете:
Проблема в том, что передача пары в функцию не дает того, что вы хотите. Но что, если бы вы могли вставить пару в функцию:
Читайте ,
feed(f, m)
как «кормm
вf
». Для того, чтобы накормить пару<x, messages>
в функциюf
, чтобы пройтиx
вf
, получить<y, message>
изf
, и вернуться<y, messages message>
.Обратите внимание, что происходит, когда вы выполняете три функции с помощью своих функций:
Во-первых: если вы переносите значение, а затем передаете полученную пару в функцию:
Это то же самое, что передать значение в функцию.
Второе: если вы кормите пару в
wrap
:Это не меняет пару.
Третье: если вы определяете функцию, которая принимает
x
и передаетg(x)
вf
:и корми пару в нее:
Это то же самое, что вводить пару
g
и вводить полученную паруf
.У вас есть большая часть монады. Теперь вам просто нужно знать о типах данных в вашей программе.
Какой тип стоимости
<x, "called f. ">
? Ну, это зависит от того, какой тип стоимостиx
. Еслиx
имеет типt
, то ваша пара является значением типа «параt
и строка». Назовите этот типM t
.M
является конструктором типа:M
один не относится к типу, ноM _
относится к типу, когда вы заполняете пробел с типом. AnM int
является парой типа int и строки. AnM string
представляет собой пару строки и строки. И т.п.Поздравляем, вы создали монаду!
Формально твоя монада - это кортеж
<M, feed, wrap>
.Монада - это кортеж, в
<M, feed, wrap>
котором:M
это конструктор типаfeed
принимает (функция, которая принимаетt
и возвращаетM u
)M t
и возвращает и возвращаетM u
.wrap
принимаетv
и возвращаетM v
.t
,u
иv
являются любыми тремя типами, которые могут или не могут быть одинаковыми. Монада удовлетворяет трем свойствам, которые вы доказали для своей конкретной монады:Подача упакованного
t
в функцию - это то же самое, что и передача развернутогоt
в функцию.Формально:
feed(f, wrap(x)) = f(x)
Кормление
M t
вwrap
ничего не делает дляM t
.Формально:
feed(wrap, m) = m
Ввод
M t
(вызовm
) в функцию, котораяt
вg
M u
(позвонитьn
) отg
n
вf
такой же как
m
вg
n
отg
n
вf
Формально:
feed(h, m) = feed(f, feed(g, m))
гдеh(x) := feed(f, g(x))
Как правило,
feed
называетсяbind
(AKA>>=
в Haskell) иwrap
называетсяreturn
.источник
Я постараюсь объяснить
Monad
в контексте Haskell.В функциональном программировании важна композиция функций. Это позволяет нашей программе состоять из небольших, легко читаемых функций.
Допустим, у нас есть две функции:
g :: Int -> String
иf :: String -> Bool
.Мы можем сделать
(f . g) x
, что так же, какf (g x)
, гдеx
этоInt
значение.При выполнении композиции / применении результата одной функции к другой важно, чтобы типы совпадали. В приведенном выше случае тип возвращаемого результата
g
должен совпадать с типом, принятымf
.Но иногда значения находятся в контекстах, и это упрощает выравнивание типов. (Наличие значений в контекстах очень полезно. Например,
Maybe Int
тип представляетInt
значение, которого может не быть,IO String
тип представляетString
значение, которое существует в результате выполнения некоторых побочных эффектов.)Допустим, у нас теперь есть
g1 :: Int -> Maybe String
иf1 :: String -> Maybe Bool
.g1
иf1
очень похожиg
иf
соответственно.Мы не можем сделать,
(f1 . g1) x
илиf1 (g1 x)
, гдеx
этоInt
значение. Тип возвращаемого результатаg1
не соответствуетf1
ожидаемому.Мы могли бы сочинять
f
иg
с.
оператором, но теперь мы не можем сочинятьf1
иg1
с оператором.
. Проблема в том, что мы не можем напрямую передать значение в контексте функции, которая ожидает значение, которое не находится в контексте.Разве не было бы неплохо, если бы мы ввели оператор для компоновки
g1
и такf1
, чтобы мы могли писать(f1 OPERATOR g1) x
?g1
возвращает значение в контексте. Значение будет вырвано из контекста и применено кf1
. И да, у нас есть такой оператор. Это<=<
.У нас также есть
>>=
оператор, который делает для нас точно такую же вещь, хотя и в несколько ином синтаксисе.Мы пишем:
g1 x >>= f1
.g1 x
этоMaybe Int
значение.>>=
Оператор помогает принять этоInt
значение из «возможно, не-там» контекста, и применить его кf1
. Результатf1
, который являетсяMaybe Bool
, будет результатом всей>>=
операции.И, наконец, почему это
Monad
полезно? ПосколькуMonad
класс типа , который определяет>>=
оператор, очень много такие же , какEq
класс типа , который определяет==
и/=
оператор.В заключение,
Monad
класс типа определяет>>=
оператор, который позволяет нам передавать значения в контексте (мы называем эти монадические значения) функциям, которые не ожидают значений в контексте. Контекст будет заботиться о.Если здесь есть одна вещь, которую нужно запомнить, это то, что
Monad
s разрешает композицию функций, которая включает значения в контекстах .источник
ТЛ; др
пролог
Оператор приложения
$
функцийканонически определен
с точки зрения приложения функции Haskell
f x
(infixl 10
).Композиция
.
определяется с точки зрения$
каки удовлетворяет эквивалентности
forall f g h.
.
является ассоциативным иid
является его правой и левой идентичностью.Тройка Клейсли
В программировании монада - это конструктор типа функтора с экземпляром класса типа монады. Существует несколько эквивалентных вариантов определения и реализации, каждый из которых несет в себе несколько разные представления об абстракции монады.
Функтор - это конструктор
f
типа* -> *
с экземпляром класса типа функтор.В дополнение к следующему статически обязательному протоколу типов экземпляры класса функторов должны подчиняться законам алгебраических функторов.
forall f g.
Функторные вычисления имеют тип
Вычисление
c r
состоит из результатовr
в контекстеc
.Унарные монадические функции или стрелки Клейсли имеют тип
Стрелки Клейси - это функции, которые принимают один аргумент
a
и возвращают монадическое вычислениеm b
.Монады канонически определены в терминах тройки Клейсли
forall m. Functor m =>
реализован как класс типа
Идентичность Клейла
return
является Клейл стрелок , которая способствует значениюt
в монадическую контекстеm
. Приложение Extension или Kleisli=<<
применяет стрелку Kleislia -> m b
к результатам вычисленийm a
.Состав Клейсли
<=<
определяется с точки зрения расширения как<=<
Составляет две стрелки Клейсли, применяя левую стрелку к результатам применения правой стрелки.Экземпляры класса типа монады должны подчиняться законам монады , наиболее элегантно изложенным в терминах композиции Клейсли:
forall f g h.
<=<
является ассоциативным иreturn
является его правой и левой идентичностью.тождественность
Тип личности
это тождественная функция на типах
Интерпретируется как функтор,
В каноническом Хаскеле тождественная монада определена
вариант
Тип опции
кодирует вычисления,
Maybe t
которые не обязательно дают результатt
, вычисления, которые могут «потерпеть неудачу». Опция монада определенаa -> Maybe b
применяется к результату, только еслиMaybe a
дает результат.Натуральные числа могут быть закодированы как целые числа, большие или равные нулю.
Натуральные числа не замкнуты при вычитании.
Опция monad охватывает базовую форму обработки исключений.
Список
Монада списка, над типом списка
и его аддитивная моноидная операция «добавить»
кодирует нелинейные вычисления,
[t]
дающие естественное количество0, 1, ...
результатовt
.Расширение
=<<
объединяет++
все списки,[b]
полученные в результате примененияf x
стрелки Клейсли,a -> [b]
к элементам[a]
в один список результатов[b]
.Пусть правильные делители натурального числа
n
будуттогда
При определении класса типа монады вместо расширения
=<<
стандарт Haskell использует свой flip, оператор связывания>>=
.Для простоты в этом объяснении используется иерархия классов типов
В Хаскеле текущая стандартная иерархия
потому что не только каждая монада является функтором, но и каждая аппликативная функция является функтором, и каждая монада также является аппликативной.
Используя монаду списка, императивный псевдокод
примерно переводится в блок do ,
эквивалентное понимание монады ,
и выражение
Понимание и понимание монады являются синтаксическим сахаром для вложенных выражений связывания. Оператор связывания используется для локального связывания имен монадических результатов.
где
Функция охраны определена
где тип блока или «пустой кортеж»
Аддитивные монады, которые поддерживают выбор и неудачу, могут быть абстрагированы с использованием класса типов
где
fail
и<|>
образуют моноидforall k l m.
и
fail
является поглощающим / уничтожающим нулевым элементом аддитивных монадЕсли в
even p
истинно, тогда сторож производит[()]
, и, по определению>>
, функцию локальной константыприменяется к результату
()
. Если false, то охранник выдает монаду listfail
([]
), которая не дает результата для стрелки Клейсли, которую нужно применить>>
, поэтомуp
она пропускается.государственный
Печально, монады используются для кодирования вычислений с состоянием.
Состояние процессора является функцией
который переходит в состояние
st
и дает результатt
. Состояниеst
может быть что угодно. Ничего, флаг, количество, массив, дескриптор, машина, мир.Тип государственных процессоров обычно называется
Монада процессора состояний - это добрый
* -> *
функторState st
. Клейсли стрелки монады состояния процессора являются функциямиВ каноническом Haskell определяется ленивая версия монады процессора состояний
Процессор состояния запускается путем предоставления начального состояния:
Доступ к состоянию обеспечивается примитивами
get
иput
методами абстрагирования по монадам с состоянием :m -> st
объявляет функциональную зависимость типа состоянияst
от монадыm
; чтоState t
, например, определит тип состояния, который будетt
уникальным.с типом устройства, используемым аналогично
void
в C.gets
часто используется с полями доступа к записи.Монад состояния, эквивалентный переменной threading
где
s0 :: Int
- одинаково прозрачный, но бесконечно более элегантный и практичныйmodify (+ 1)
является вычислением типаState Int ()
, за исключением его эффекта, эквивалентногоreturn ()
.Монадический закон ассоциативности можно записать в терминах
>>=
forall m f g.
или
Как и в программировании, ориентированном на выражения (например, Rust), последний оператор блока представляет его результат. Оператор связывания иногда называют «программируемой точкой с запятой».
Примитивы структуры управления итерациями из структурированного императивного программирования эмулируются монадически
Ввод, вывод
Монада процессора состояния мира ввода / вывода - это примирение чистого Хаскелла и реального мира функциональной денотативной и императивной операционной семантики. Близкий аналог собственно строгой реализации:
Взаимодействие облегчают нечистые примитивы
Примесь кода, который использует
IO
примитивы, постоянно протоколируется системой типов. Потому что чистота потрясающая, что происходит внутриIO
, остается внутриIO
.Или, по крайней мере, должен.
Подпись типа программы на Haskell
расширяется до
Функция, которая преобразует мир.
эпилог
Категория, объекты которой относятся к типам Haskell, а морфизмы - это функции между типами Haskell - это «быстрая и свободная» категория
Hask
.Функтор
T
- это отображение категорииC
в категориюD
; для каждого объекта вC
объекте вD
и для каждого морфизма в
C
морфизме вD
где
X
,Y
объекты вC
.HomC(X, Y)
это класс гомоморфизм всех морфизмовX -> Y
вC
. Функтор должен сохранять индивидуальность и композицию морфизма, «структуру»C
, вD
.Категория Клейла из категории
C
даются Клейли тройкиэндофунктора
(
f
), тождественный морфизмeta
(return
) и оператор расширения*
(=<<
).У каждого Клейсли морфизм в
Hask
оператором расширения
дается морфизм в
Hask
категории КлейслиКомпозиция в категории Клейсли
.T
приведена в терминах расширенияи удовлетворяет категории аксиомы
который, применяя преобразования эквивалентности
с точки зрения расширения даны канонически
Монады также могут быть определены в терминах не расширения Клейсляна, а естественного преобразования
mu
в вызываемом программированииjoin
. Монада определяетсяmu
как тройка над категориейC
эндофунктораи две природные трансформации
удовлетворяя эквивалентности
Класс типа монады затем определяется
Каноническая
mu
реализация варианта монады:concat
функцияявляется
join
списком монады.Реализации
join
могут быть переведены из формы расширения с использованием эквивалентностиОбратный перевод из
mu
формы расширения в формуФилипп Вадлер: монады для функционального программирования
Саймон Л. Пейтон Джонс, Филипп Вадлер: императивное функциональное программирование
Джонатан MD - Хилл, Кейт Кларк: Введение в теорию категории, теории категории монад, и их отношения к функциональному программированию "
Клейсли категория
Эудженио Могги: понятия вычисления и монады
Что не монада
от обобщающих монад до стрел Джона Хьюза
источник
Мир нуждается в другом сообщении в блоге монад, но я думаю, что это полезно для идентификации существующих монад в дикой природе.
источник
http://code.google.com/p/monad-tutorial/ находится в стадии разработки, чтобы ответить именно на этот вопрос.
источник
Позвольте ниже "
{| a |m}
" представить некоторую часть монадических данных. Тип данных, который рекламируетa
:Функция,
f
знает, как создать монаду, если бы она имелаa
:Здесь мы видим функцию,
f
пытается оценить монаду, но получает упрек.Функция,
f
находит способ извлечьa
с помощью>>=
.Мало ли
f
знает, монада и>>=
находятся в сговоре.Но о чем они на самом деле говорят? Ну, это зависит от монады. Разговор только в абстрактной форме имеет ограниченное применение; Вы должны иметь некоторый опыт работы с конкретными монадами, чтобы конкретизировать понимание.
Например, тип данных Maybe
имеет экземпляр монады, который будет действовать следующим образом ...
Где, если дело
Just a
Но для случая
Nothing
Таким образом, монада Maybe позволяет продолжить вычисление, если оно действительно содержит
a
объявленную им информацию, но прерывает вычисление, если этого не происходит. Результат, однако, все еще является частью монадических данных, но не выводf
. По этой причине монада Maybe используется для представления контекста сбоя.Разные монады ведут себя по-разному. Списки - это другие типы данных с монадическими экземплярами. Они ведут себя следующим образом:
В этом случае функция знала, как составить список из своего ввода, но не знала, что делать с дополнительным вводом и дополнительными списками. Связка
>>=
, вырученнаяf
путем объединения нескольких выходов. Я включил этот пример, чтобы показать, что, хотя>>=
он отвечает за извлечениеa
, он также имеет доступ к возможному выходу привязкиf
. Действительно, он никогда не извлечет ничего,a
если не знает, что конечный результат имеет тот же тип контекста.Существуют и другие монады, которые используются для представления различных контекстов. Вот некоторые характеристики еще нескольких.
IO
Монады фактически не имеетa
, но он знает парень , и получите , чтоa
для вас. УState st
монады есть тайник,st
который она пропуститf
под столом, хотяf
только что пришла проситьa
.Reader r
Монада подобнаState st
, хотя она позволяет толькоf
смотреть наr
.Суть всего этого в том, что любой тип данных, который объявлен монадой, объявляет некоторый контекст извлечения значения из монады. Большой выигрыш от всего этого? Ну, это достаточно просто, чтобы составить расчет с каким-то контекстом. Однако, это может привести к путанице при объединении нескольких вычислений, нагруженных контекстом. Операции монады позаботятся о разрешении взаимодействий контекста, чтобы программисту не пришлось это делать.
Обратите внимание, что использование
>>=
облегчает беспорядок, отнимая часть автономии отf
. То есть, в приведенном выше случае,Nothing
например,f
больше не нужно решать, что делать в случаеNothing
; это закодировано в>>=
. Это компромисс. Если это было необходимо для того,f
чтобы решить, что делать в случаеNothing
, тогдаf
должна была быть функция отMaybe a
доMaybe b
. В этом случаеMaybe
быть монадой не имеет значения.Однако обратите внимание, что иногда тип данных не экспортирует свои конструкторы (глядя на ваш IO), и если мы хотим работать с объявленным значением, у нас нет другого выбора, кроме как работать с его монадическим интерфейсом.
источник
Монада - это вещь, используемая для инкапсуляции объектов, которые имеют изменяющееся состояние. Это чаще всего встречается в языках, которые в противном случае не позволяют иметь изменяемое состояние (например, Haskell).
Примером может служить файловый ввод / вывод.
Вы могли бы использовать монаду для файлового ввода-вывода, чтобы изолировать изменяющуюся природу состояния только для кода, который использовал монаду. Код внутри Monad может эффективно игнорировать изменяющееся состояние мира за пределами Monad - это значительно упрощает анализ общего эффекта вашей программы.
источник