С точки зрения того, что программист ООП понимает (без какой-либо функциональной основы программирования), что такое монада?
Какую проблему он решает и какие места он использует чаще всего?
РЕДАКТИРОВАТЬ:
Чтобы прояснить то понимание, которое я искал, допустим, вы конвертировали приложение FP с монадами в приложение ООП. Что бы вы сделали, чтобы перенести обязанности монад в приложение ООП?
Ответы:
ОБНОВЛЕНИЕ: Этот вопрос был темой очень длинной серии блогов, которую вы можете прочитать на Monads - спасибо за отличный вопрос!
Монада - это «усилитель» типов, который подчиняется определенным правилам и в котором предусмотрены определенные операции .
Во-первых, что такое «усилитель типов»? Под этим я подразумеваю некоторую систему, которая позволяет вам брать тип и превращать его в более специальный тип. Например, в C # рассмотрим
Nullable<T>
. Это усилитель типов. Это позволяет вам взять тип, скажемint
, и добавить новую возможность к этому типу, а именно, что теперь он может быть нулевым, если не мог раньше.В качестве второго примера рассмотрим
IEnumerable<T>
. Это усилитель типов. Это позволяет вам взять тип, скажем,string
и добавить к этому типу новую возможность, а именно то, что теперь вы можете создавать последовательность строк из любого числа отдельных строк.Каковы "определенные правила"? Вкратце, существует разумный способ для функций базового типа работать с усиленным типом таким образом, чтобы они следовали обычным правилам функциональной композиции. Например, если у вас есть функция целых чисел, скажем
тогда соответствующая функция on
Nullable<int>
может заставить все операторы и вызовы там работать вместе «так же, как раньше».(Это невероятно расплывчато и неточно; вы попросили объяснения, в котором ничего не говорилось о знании функциональной композиции.)
Каковы "операции"?
Существует операция «unit» (иногда вызывающая путаницу, называемая операцией «return»), которая берет значение из простого типа и создает эквивалентное монадическое значение. Это, в сущности, позволяет получить значение неусиленного типа и превратить его в значение усиленного типа. Это может быть реализовано как конструктор на языке ОО.
Существует операция «связать», которая принимает монадическое значение и функцию, которая может преобразовать значение и возвращает новое монадическое значение. Привязка является ключевой операцией, которая определяет семантику монады. Это позволяет нам преобразовывать операции над неусиленным типом в операции над усиленным типом, которые подчиняются правилам функциональной композиции, упомянутым ранее.
Часто есть способ вернуть неусиленный тип из усиленного. Строго говоря, для этой операции не обязательно иметь монаду. (Хотя это необходимо, если вы хотите иметь комаду . Мы не будем обсуждать это далее в этой статье.)
Опять же, возьмите
Nullable<T>
в качестве примера. Вы можете превратитьint
вNullable<int>
конструктор. Компилятор C # позаботится о наиболее обнуляемом «поднятии» за вас, но если этого не произойдет, преобразование подъема будет простым: операция, скажем,превращается в
И превращение
Nullable<int>
обратно в этоint
сделано сValue
собственностью.Это преобразование функции является ключевым битом. Обратите внимание, как фактическая семантика обнуляемой операции - что операция над
null
объектом распространяетсяnull
- преобразуется в преобразование. Мы можем обобщить это.Предположим, у вас есть функция от
int
доint
, как наш оригиналM
. Вы можете легко превратить это в функцию, которая принимаетint
и возвращает a,Nullable<int>
потому что вы можете просто запустить результат через конструктор, допускающий значение NULL. Теперь предположим, что у вас есть метод высшего порядка:Видишь, что ты можешь с этим сделать? Любой метод, который принимает
int
и возвращаетint
, или принимаетint
и возвращает,Nullable<int>
может теперь применять к нему семантику, допускающую значение NULL .Кроме того: предположим, у вас есть два метода
а ты хочешь их составить
То есть
Z
это составX
иY
. Но вы не можете этого сделать, потому чтоX
принимаетint
иY
возвращаетNullable<int>
. Но так как у вас есть операция «связать», вы можете сделать эту работу:Операция связывания с монадой - это то, что заставляет работать функции на усиленных типах. «Правила», о которых я писал выше, заключаются в том, что монада сохраняет правила нормальной композиции функций; что сочинение с использованием тождественных функций приводит к исходной функции, эта композиция является ассоциативной и так далее.
В C # «Bind» называется «SelectMany». Посмотрите, как это работает на монаде последовательности. Нам нужно иметь две вещи: превратить значение в последовательность и связать операции с последовательностями. В качестве бонуса у нас также есть «превратить последовательность обратно в значение». Эти операции:
Правило обнуляемой монады состояло в том, чтобы «объединить две функции, которые производят обнуляемые вместе, проверить, не приводит ли внутренняя к нулю; если это так, вывести нулевое, если нет, то вызвать внешнюю с результатом». Это желаемая семантика обнуляемого.
Правило монады последовательностей состоит в том, чтобы «объединить две функции, которые создают последовательности, применить внешнюю функцию к каждому элементу, созданному внутренней функцией, и затем объединить все полученные последовательности вместе». Основная семантика монад отражена в методах
Bind
/SelectMany
; это метод, который говорит вам, что на самом деле означает монада .Мы можем сделать еще лучше. Предположим, у вас есть последовательность целых чисел и метод, который принимает целые числа и приводит к последовательностям строк. Мы могли бы обобщить операцию связывания, чтобы разрешить составление функций, которые принимают и возвращают различные усиленные типы, при условии, что входы одного совпадают с выходами другого:
Так что теперь мы можем сказать: «амплифицировать эту группу отдельных целых чисел в последовательность целых чисел. Преобразовать это конкретное целое число в группу строк, усиленную в последовательность строк. Теперь соедините обе операции вместе: амплифицируйте эту группу целых чисел в конкатенацию все последовательности строк. " Монады позволяют вам составлять ваши усиления.
Это все равно, что спросить: «Какие проблемы решает шаблон синглтона?», Но я попробую.
Монады обычно используются для решения таких проблем, как:
C # использует монады в своем дизайне. Как уже упоминалось, обнуляемый шаблон очень похож на «возможно, монаду». LINQ полностью построен из монад;
SelectMany
метод , что делает семантическую работу состава операций. (Эрик Мейер любит указывать, что каждая функция LINQ может быть реализованаSelectMany
; все остальное - просто удобство.)Большинство языков ООП не имеют достаточно богатой системы типов, чтобы непосредственно представлять сам образец монады; вам нужна система типов, которая поддерживает типы более высоких типов, чем универсальные типы. Так что я бы не стал это делать. Скорее, я бы реализовал универсальные типы, которые представляют каждую монаду, и реализовал бы методы, которые представляют три нужные вам операции: преобразование значения в усиленное значение, (возможно) преобразование усиленного значения в значение и преобразование функции с неусиленными значениями в функция на усиленные значения.
Хорошее начало - как мы реализовали LINQ в C #. Изучить
SelectMany
метод; это ключ к пониманию того, как работает монада последовательностей в C #. Это очень простой метод, но очень мощный!Предложено, дальнейшее чтение:
источник
Зачем нам нужны монады?
Тогда у нас есть первая большая проблема. Это программа:
f(x) = 2 * x
g(x,y) = x / y
Как мы можем сказать, что должно быть выполнено первым ? Как мы можем сформировать упорядоченную последовательность функций (то есть программу ), используя не более чем функции?
Решение: составить функции . Если хочешь сначала,
g
а потомf
просто пишиf(g(x,y))
. Да, но ...Больше проблем: некоторые функции могут не работать (т.е.
g(2,0)
делятся на 0). У нас нет «исключений» в ФП . Как мы это решаем?Решение: Давайте позволим функциям возвращать два вида вещей : вместо того, чтобы иметь
g : Real,Real -> Real
(функция из двух действительных в действительное число), давайте позволимg : Real,Real -> Real | Nothing
(функция из двух действительных в (действительное или ничего)).Но функции должны (быть проще) возвращать только одну вещь .
Решение: давайте создадим новый тип данных, которые будут возвращаться, « тип бокса », который может быть реальным или быть просто ничем. Следовательно, мы можем иметь
g : Real,Real -> Maybe Real
. Да, но ...Что происходит сейчас с
f(g(x,y))
?f
не готов потреблятьMaybe Real
. И мы не хотим менять каждую функцию, с которой мы можем соединиться,g
чтобы использовать aMaybe Real
.Решение: у нас есть специальная функция для «соединения» / «создания» / «связывания» функций . Таким образом, мы можем за кулисами адаптировать вывод одной функции для передачи следующей.
В нашем случае:
g >>= f
(подключение / Composeg
кf
). Мы хотим>>=
получитьg
выходные данные, проверить их и, в случае, если ониNothing
просто не вызываютf
и не возвращаютNothing
; или наоборот, извлеките в штучной упаковкеReal
и кормитьf
его. (Этот алгоритм является просто реализацией>>=
дляMaybe
типа).Возникают многие другие проблемы, которые могут быть решены с использованием этого же шаблона: 1. Используйте «коробку» для кодификации / хранения различных значений / значений, и такие функции
g
возвращают эти «коробочные значения». 2. Иметь композиторов / компоновщиков,g >>= f
чтобы помочь соединитьg
выводf
с вводом, поэтому нам не нужно ничего менятьf
.Замечательные проблемы, которые могут быть решены с помощью этой техники:
имея глобальное состояние, что каждая функция в последовательности функций («программа») может совместно использовать: решение
StateMonad
.Нам не нравятся «нечистые функции»: функции, которые дают разные выходные данные для одного и того же ввода. Поэтому давайте пометим эти функции, заставив их возвращать теговое / коробочное значение:
IO
monad.Полное счастье !!!!
источник
State
иIO
ни с одним из них, а также с точным значением слова «монада»Я бы сказал, что ближайшая аналогия с монадами - это « шаблон команд ».
В шаблоне команды вы помещаете обычное выражение или выражение в объект команды . Объект команды предоставляет метод execute, который выполняет упакованный оператор. Таким образом, оператор превращается в объекты первого класса, которые могут передаваться и выполняться по желанию. Команды могут быть составлены так, что вы можете создать программный объект путем объединения и вложения командных объектов.
Команды выполняются отдельным объектом, вызывающим . Преимущество использования шаблона команды (а не просто выполнения ряда обычных операторов) состоит в том, что разные инициаторы могут применять разную логику к тому, как команды должны выполняться.
Шаблон команды может использоваться для добавления (или удаления) языковых функций, которые не поддерживаются языком хоста. Например, в гипотетическом языке OO без исключений вы можете добавить семантику исключений, предоставляя командам методы try и throw. Когда команда вызывает throw, вызывающий возвращается к списку (или дереву) команд до последнего вызова «try». И наоборот, вы можете удалить семантику исключений из языка (если вы считаете, что исключения плохие ), перехватывая все исключения, создаваемые каждой отдельной командой, и превращая их в коды ошибок, которые затем передаются следующей команде.
Еще более причудливая семантика выполнения, такая как транзакции, недетерминированное выполнение или продолжения, может быть реализована таким образом на языке, который не поддерживает его изначально. Это довольно мощный паттерн, если подумать.
Теперь в действительности шаблоны команд не используются в качестве общей языковой функции, подобной этой. Затраты на преобразование каждого оператора в отдельный класс приведут к невыносимому количеству шаблонного кода. Но в принципе это может быть использовано для решения тех же проблем, что и монады для решения в fp.
источник
С точки зрения программирования OO, монада представляет собой интерфейс (или скорее примесь), параметризовано типа, с двумя методами,
return
иbind
которые описывают:Решаемая проблема - это проблема того же типа, которую вы ожидаете от любого интерфейса, а именно: «У меня есть куча разных классов, которые делают разные вещи, но, кажется, делают эти разные вещи способом, который имеет основное сходство. могу ли я описать это сходство между ними, даже если сами классы на самом деле не являются подтипами чего-либо более близкого, чем сам класс «Объект»? "
Более конкретно,
Monad
«интерфейс» похожIEnumerator
илиIIterator
в том смысле, что он принимает тип, который сам принимает тип. Основная «точка»Monad
хотя - это возможность соединять операции, основанные на внутреннем типе, даже с точки зрения наличия нового «внутреннего типа», сохраняя или даже улучшая информационную структуру основного класса.источник
return
на самом деле не будет методом для монады, потому что он не принимает экземпляр монады в качестве аргумента. (т.е.: это не / я)У вас есть недавняя презентация Кристофера Лиги « Монадология - профессиональная помощь по типу тревоги » (12 июля 2010 г.), которая довольно интересна по темам продолжения и монады. Видео с этой (слайд-шоу) презентацией действительно доступно на vimeo . Партия монады начинается примерно через 37 минут в этом часовом видео и начинается со слайда 42 из его 58 презентаций слайдов.
Он представлен как «ведущий шаблон проектирования для функционального программирования», но в примерах используется язык Scala, который является одновременно ООП и функциональным.
Вы можете прочитать больше о Monad в Scala в посте блога « Monads - еще один способ абстрагировать вычисления в Scala », от Дебасиша Гоша (27 марта 2008 г.).
Так, например (в Scala):
Option
это монадаList
это монадаMonad имеет большое значение в Scala из-за удобного синтаксиса, созданного для использования преимуществ структур Monad:
for
понимание в Scala :переводится компилятором в:
Ключевая абстракция - это та
flatMap
, которая связывает вычисления через цепочку.Каждый вызов
flatMap
возвращает один и тот же тип структуры данных (но различного значения), который служит входом для следующей команды в цепочке.В приведенном выше фрагменте flatMap принимает в качестве входных данных замыкание
(SomeType) => List[AnotherType]
и возвращает aList[AnotherType]
. Важно отметить, что все flatMaps принимают тот же тип замыкания, что и ввод, и возвращают тот же тип, что и вывод.Это то, что «связывает» поток вычислений - каждый элемент последовательности в для понимания должен соблюдать это ограничение типа.
Если вы берете две операции (которые могут потерпеть неудачу) и передаете результат третьей, например:
но, не используя Monad, вы получаете запутанный ООП-код, такой как:
в то время как с Monad вы можете работать с фактическими типами (
Venue
,User
), как и все операции, и сохранять скрытые данные проверки Option, все из-за плоских карт синтаксиса for:Часть yield будет выполнена, только если все три функции имеют
Some[X]
; любойNone
будет напрямую возвращенconfirm
.Так:
Кстати, Monad - это не только модель вычислений, используемая в FP:
источник
Чтобы уважать быстрых читателей, я сначала начну с точного определения, продолжу с более быстрого «простого английского» объяснения, а затем перейду к примерам.
Вот краткое и точное определение, слегка перефразированное:
Итак, простыми словами, монада - это правило для перехода от любого типа
X
к другому типуT(X)
, и правило для перехода от двух функцийf:X->T(Y)
иg:Y->T(Z)
(которые вы хотели бы создать, но не можете) к новой функцииh:X->T(Z)
. Что, однако, не является композицией в строгом математическом смысле. Мы в основном «сгибаем» состав функции или переопределяем, как составляются функции.Кроме того, мы требуем, чтобы правило сочинения монады удовлетворяло «очевидным» математическим аксиомам:
f
с,g
а затем сh
(извне) должна быть такой же, как иg
с,h
а затем сf
(изнутри).f
составление с функцией идентичности с обеих сторон должно датьf
.Опять же, простыми словами, мы не можем просто сойти с ума, переопределив нашу композицию функций так, как нам нравится:
f(g(h(k(x)))
, и не беспокоиться об указании порядка составления пар функций. Поскольку правило монады только предписывает, как составлять пару функций без этой аксиомы, нам нужно знать, какая пара составлена первой и так далее. (Обратите внимание, что это свойство отличается от того, что свойство commutativity,f
с которымg
были созданы, было таким же, как свойствоg
сf
, с которым не требуется).Итак, еще раз вкратце: монада - это правило расширения типа и составления функций, удовлетворяющих двум аксиомам - ассоциативности и унитальному свойству.
С практической точки зрения, вы хотите, чтобы монада была реализована для вас языком, компилятором или фреймворком, который позаботился бы о создании функций для вас. Таким образом, вы можете сосредоточиться на написании логики своей функции, а не беспокоиться о том, как реализовано их выполнение.
По сути, это в двух словах.
Будучи профессиональным математиком, я предпочитаю избегать называть
h
«композицией»f
иg
. Потому что математически это не так. Называя это «композиция» неправильно предполагает, чтоh
это истинная математическая композиция, а это не так. Это даже не однозначно определяетсяf
иg
. Вместо этого это результат нового "правила составления" нашей монады. Который может полностью отличаться от фактического математического состава, даже если последний существует!Чтобы сделать его менее сухим, я попытаюсь проиллюстрировать это на примере, который я аннотирую небольшими разделами, чтобы вы могли перейти непосредственно к делу.
Исключение как примеры монады
Предположим, мы хотим составить две функции:
Но
f(0)
не определено, поэтому выдается исключениеe
. Тогда как вы можете определить композиционную ценностьg(f(0))
? Брось исключение, конечно же! Может быть, то же самоеe
. Возможно новое обновленное исключениеe1
.Что именно здесь происходит? Во-первых, нам нужны новые значения исключений (разные или одинаковые). Вы можете назвать их
nothing
или какnull
угодно, но суть остается прежней - они должны быть новыми значениями, например, это не должно бытьnumber
в нашем примере здесь. Я предпочитаю не называть их,null
чтобы избежать путаницы с тем, какnull
можно реализовать на любом конкретном языке. В равной степени я предпочитаю избегать,nothing
потому что это часто связано с темnull
, что, в принципе, и является тем, чтоnull
следует делать, однако этот принцип часто отклоняется по любым практическим причинам.Что именно является исключением?
Для любого опытного программиста это тривиальный вопрос, но я бы хотел сказать несколько слов, чтобы погасить червя путаницы:
Исключение - это объект, инкапсулирующий информацию о том, как произошел неверный результат выполнения.
Это может варьироваться от отбрасывания любых деталей и возврата одного глобального значения (например,
NaN
илиnull
) до генерации длинного списка журналов или того, что именно произошло, отправки его в базу данных и репликации по всему распределенному слою хранения данных;)Важное различие между этими двумя крайними примерами исключения состоит в том, что в первом случае нет побочных эффектов . Во втором есть. Что подводит нас к вопросу (тысяча долларов):
Разрешены ли исключения в чистых функциях?
Краткий ответ : да, но только когда они не приводят к побочным эффектам.
Более длинный ответ. Чтобы быть чистым, вывод вашей функции должен быть однозначно определен ее вводом. Поэтому мы изменяем нашу функцию
f
, отправляя0
новое абстрактное значение,e
которое мы называем исключением. Мы удостоверяемся, что значение неe
содержит никакой внешней информации, которая не определяется однозначно нашим вводомx
. Итак, вот пример исключения без побочных эффектов:И вот один с побочным эффектом:
На самом деле, это имеет только побочные эффекты, если это сообщение может измениться в будущем. Но если гарантируется, что оно никогда не изменится, это значение станет уникально предсказуемым, и, следовательно, побочный эффект отсутствует.
Чтобы сделать это еще глупее. Функция, возвращающаяся
42
всегда, явно чиста. Но если кто-то сумасшедший решит создать42
переменную, значение которой может измениться, та же самая функция перестанет быть чистой в новых условиях.Обратите внимание, что для простоты я использую буквенную нотацию объекта. К сожалению, вещи перепутаны в таких языках, как JavaScript, где
error
тип не ведет себя так, как мы хотим здесь, относительно композиции функций, в то время как фактические типы любятnull
илиNaN
не ведут себя таким образом, а скорее проходят через некоторые искусственные и не всегда интуитивно понятные преобразования типов.Тип расширения
Поскольку мы хотим изменить сообщение внутри нашего исключения, мы действительно объявляем новый тип
E
для всего объекта исключения, а затем это то, чтоmaybe number
делает, кроме его запутанного имени, которое должно быть либо типа,number
либо нового типа исключенияE
, так что это действительно союзnumber | E
изnumber
иE
. В частности, это зависит от того, как мы хотим построитьE
, что не предлагается и не отражено в названииmaybe number
.Что такое функциональная композиция?
Это математические функции операции , принимающие
f: X -> Y
иg: Y -> Z
и построение их состава как функция ,h: X -> Z
удовлетворяющаяh(x) = g(f(x))
. Проблема с этим определением возникает, когда результатf(x)
не разрешен в качестве аргументаg
.В математике эти функции не могут быть составлены без дополнительной работы. Строго математическое решение для нашего приведенного выше примера
f
иg
заключается в удалении0
из набора определенийf
. С этим новым набором определений (новый, более строгий типx
)f
становится совместимым сg
.Однако в программировании не очень практично ограничивать набор таких определений
f
. Вместо этого могут быть использованы исключения.Или как другой подход, искусственные ценности создаются , как
NaN
,undefined
,null
, иInfinity
т.д. Таким образом , вы оцениваете1/0
кInfinity
и1/-0
к-Infinity
. И затем принудительно верните новое значение в ваше выражение, вместо того, чтобы вызывать исключение. Приводя к результатам вы можете или не можете найти предсказуемые:И мы вернулись к обычным номерам, готовым двигаться дальше;)
JavaScript позволяет нам продолжать выполнение числовых выражений любой ценой без ошибок, как в примере выше. Это означает, что он также позволяет составлять функции. Именно это и есть монада - это правило составлять функции, удовлетворяющие аксиомам, как определено в начале этого ответа.
Но является ли правило составления функции, возникающей из реализации JavaScript для обработки числовых ошибок, монадой?
Чтобы ответить на этот вопрос, все, что вам нужно, это проверить аксиомы (оставленные здесь как упражнение, но не являющиеся частью вопроса;).
Можно ли использовать исключение для создания монады?
Более того, более полезной монадой было бы правило, предписывающее, что если
f
для некоторых выдается исключение, то же самоеx
происходит и с любой композициейg
. Плюс сделайте исключениеE
глобально уникальным только с одним возможным значением ( конечный объект в теории категорий). Теперь две аксиомы мгновенно проверяются, и мы получаем очень полезную монаду. И результат - то, что известно как возможно монада .источник
Монада - это тип данных, который инкапсулирует значение и к которому, по сути, можно применить две операции:
return x
создает значение типа монады, которое инкапсулируетx
m >>= f
(читается как «оператор связывания») применяет функциюf
к значению в монадеm
Вот что такое монада. Есть еще несколько технических особенностей , но в основном эти две операции определяют монаду. Реальный вопрос: «Что делает монада ?», И это зависит от монада - списки - это монады, Maybes - это монады, операции ввода-вывода - это монады. Все, что это означает, когда мы говорим, что это монады, это то, что они имеют интерфейс монады
return
и>>=
.источник
bind
функции, которая должна быть определена для каждого монадического типа, не так ли? Это было бы хорошей причиной, чтобы не путать связывание с композицией, поскольку существует единственное определение для композиции, хотя для функции связывания не может быть только одного определения, для монадического типа - по одному, если я правильно понимаю.Из википедии :
Я считаю, что это очень хорошо объясняет.
источник
Я постараюсь дать кратчайшее определение, которое мне удастся использовать, используя термины ООП:
Универсальный класс
CMonadic<T>
- это монада, если он определяет по крайней мере следующие методы:и если следующие законы применяются для всех типов T и их возможных значений t
левая личность:
правильная идентичность
ассоциативность:
Примеры :
Монада List может иметь:
И flatMap в списке [1,2,3] может работать так:
Итерируемые и наблюдаемые также можно сделать монадическими, а также обещаниями и заданиями.
Комментарий :
Монады не так сложны.
flatMap
Функция очень много , как более часто встречаетсяmap
. Он получает аргумент функции (также известный как делегат), который он может вызывать (немедленно или позже, ноль или более раз) со значением, полученным из универсального класса. Ожидается, что переданная функция также обернет свое возвращаемое значение в тот же тип универсального класса. Чтобы помочь с этим, он предоставляетcreate
конструктор, который может создать экземпляр этого универсального класса из значения. Возвращаемый результат flatMap также является универсальным классом того же типа, часто упаковывая те же значения, которые содержались в результатах возврата одного или нескольких приложений flatMap, в ранее содержащиеся значения. Это позволяет вам связывать flatMap столько, сколько вы хотите:Так уж получилось, что этот родовой класс полезен в качестве базовой модели для огромного количества вещей. Это (вместе с жаргонизмами теории категорий) является причиной, по которой монады кажутся такими трудными для понимания или объяснения. Они очень абстрактные вещи и становятся очевидно полезными только когда они специализируются.
Например, вы можете моделировать исключения, используя монадические контейнеры. Каждый контейнер будет содержать результат операции или возникшую ошибку. Следующая функция (делегат) в цепочке обратных вызовов flatMap будет вызываться, только если предыдущая упаковала значение в контейнер. В противном случае, если ошибка была запакована, ошибка будет продолжать распространяться по цепочечным контейнерам, пока не будет найден контейнер, к которому прикреплена функция обработчика ошибок с помощью вызываемого метода
.orElse()
(такой метод будет разрешенным расширением)Примечания : Функциональные языки позволяют вам писать функции, которые могут работать с любым видом монадического универсального класса. Чтобы это работало, нужно написать общий интерфейс для монад. Я не знаю, возможно ли написать такой интерфейс на C #, но насколько я знаю, это не так:
источник
Имеет ли монада «естественную» интерпретацию в ОО, зависит от монады. В таком языке, как Java, вы можете перевести возможную монаду в язык проверки на наличие нулевых указателей, чтобы неудачные вычисления (т. Е. Ничего не производящие в Haskell) испускали нулевые указатели в качестве результатов. Вы можете перевести монаду состояния на язык, созданный путем создания изменяемой переменной и методов для изменения ее состояния.
Монада - это моноид в категории эндофункторов.
Информация, которую складывает предложение, очень глубокая. И вы работаете в монаде с любым императивным языком. Монада - это «упорядоченный» предметно-ориентированный язык. Он удовлетворяет определенным интересным свойствам, которые в совокупности превращают монаду в математическую модель «императивного программирования». Haskell позволяет легко определять маленькие (или большие) императивные языки, которые можно комбинировать различными способами.
Как OO-программист, вы используете иерархию классов вашего языка для организации видов функций или процедур, которые можно вызывать в контексте, то, что вы называете объектом. Монада также является абстракцией этой идеи, поскольку различные монады могут комбинироваться произвольно, эффективно «импортируя» все методы субмонады в область видимости.
Архитектурно, тогда каждый использует сигнатуры типа, чтобы явно выразить, какие контексты могут использоваться для вычисления значения.
Для этой цели можно использовать монадные трансформаторы, и существует коллекция высокого качества всех «стандартных» монад:
с соответствующими монадными трансформаторами и типами классов. Классы типов допускают дополнительный подход к объединению монад, объединяя их интерфейсы, так что конкретные монады могут реализовать стандартный интерфейс для «вида» монады. Например, модуль Control.Monad.State содержит класс MonadState sm, а (State s) является экземпляром формы
Длинная история состоит в том, что монада - это функтор, который присоединяет «контекст» к значению, который имеет способ ввести значение в монаду, и который имеет способ оценивать значения по отношению к контексту, связанному с ним, по крайней мере ограниченным образом.
Так:
является функцией, которая вводит значение типа a в монадное «действие» типа m a.
это функция, которая выполняет монадное действие, оценивает его результат и применяет функцию к результату. Особенность (>> =) в том, что результат находится в той же монаде. Другими словами, в m >> = f (>> =) извлекает результат из m и связывает его с f, так что результат находится в монаде. (В качестве альтернативы мы можем сказать, что (>> =) тянет f в m и применяет его к результату.) Как следствие, если у нас есть f :: a -> mb и g :: b -> mc, мы можем «последовательность» действий:
Или, используя «сделать нотацию»
Тип для (>>) может быть светящимся. это
Это соответствует оператору (;) в процедурных языках, таких как C. Это позволяет делать обозначения, такие как:
В математической и философской логике у нас есть фреймы и модели, которые «естественно» моделируются с помощью монадизма. Интерпретация - это функция, которая изучает область модели и вычисляет истинностное значение (или обобщения) предложения (или формулы при обобщениях). В модальной логике необходимости мы можем сказать, что суждение необходимо, если оно истинно в «каждом возможном мире» - если оно верно в отношении любой допустимой области. Это означает, что модель на языке для предложения может быть преобразована в модель, область которой состоит из набора различных моделей (одна, соответствующая каждому возможному миру). Каждая монада имеет метод с именем «join», который выравнивает слои, что означает, что каждое монадное действие, результатом которого является монадное действие, может быть встроено в монаду.
Что еще более важно, это означает, что монада закрывается в результате операции «укладки слоев». Вот как работают монадные преобразователи: они объединяют монады, предоставляя «похожие» методы для таких типов, как
так что мы можем преобразовать действие в (MaybeT m) в действие в m, эффективно сворачивая слои. В этом случае runMaybeT :: MaybeT ma -> m (может быть, a) - это наш метод соединения. (MaybeT m) является монадой, а MaybeT :: m (Maybe a) -> MaybeT ma фактически является конструктором для нового типа действия монады в m.
Свободная монада для функтора - это монада, генерируемая суммированием f, из которой следует, что каждая последовательность конструкторов для f является элементом свободной монады (или, точнее, той же самой формы, что и дерево последовательностей конструкторов для е). Свободные монады являются полезным методом для построения гибких монад с минимальным количеством котельной плиты. В программе на Haskell я мог бы использовать свободные монады для определения простых монад для «системного программирования высокого уровня», чтобы помочь поддерживать безопасность типов (я просто использую типы и их объявления. Реализации просты с использованием комбинаторов):
Монадизм - это основополагающая архитектура для того, что вы могли бы назвать шаблоном «интерпретатор» или «команда», абстрагируясь до его наиболее четкой формы, поскольку каждое монадическое вычисление должно быть «выполнено», по крайней мере, тривиально. (Система времени выполнения запускает для нас монаду ввода-вывода и является точкой входа в любую программу на Haskell. IO «запускает» остальные вычисления, выполняя действия IO по порядку).
Тип для соединения также является тем, где мы получаем утверждение, что монада является моноидом в категории эндофункторов. Присоединение, как правило, более важно для теоретических целей, в силу его типа. Но понимание типа означает понимание монад. Соединяющие и монадные типы, сходные с трансформатором, по сути являются композициями эндофункторов в смысле композиции функций. Чтобы поместить это в псевдо-язык, похожий на Haskell,
Foo :: m (ma) <-> (m. M) a
источник
Монада - это массив функций
(Pst: массив функций - это просто вычисление).
На самом деле, вместо истинного массива (одна функция в одном массиве ячеек) у вас есть эти функции, связанные другой функцией >> =. >> = позволяет адаптировать результаты функции i к функции подачи i + 1, выполнять вычисления между ними или даже не вызывать функцию i + 1.
Типы, используемые здесь, являются «типами с контекстом». Это значение с тегом. Цепные функции должны принимать «голое значение» и возвращать тегированный результат. Одной из обязанностей >> = является извлечение обнаженного значения из его контекста. Также есть функция return, которая принимает голое значение и помещает его с тегом.
Пример с Возможно . Давайте использовать его для хранения простого целого числа, по которому производим вычисления.
Чтобы показать, что монады являются массивом функций с вспомогательными операциями, рассмотрим эквивалент, приведенный выше, просто используя реальный массив функций.
И это будет использоваться так:
источник
>>=
это оператор\x -> x >>= k >>= l >>= m
это массив функций, то естьh . g . f
, который не включает в себя монады вообще.С точки зрения ОО, монада - это свободный контейнер.
Минимальным требованием является определение того,
class <A> Something
что поддерживает конструкторSomething(A a)
и хотя бы один метод.Something<B> flatMap(Function<A, Something<B>>)
Возможно, он также считает, есть ли в вашем классе монады какие-либо методы с сигнатурой
Something<B> work()
которая сохраняет правила класса - компилятор запекает в flatMap во время компиляции.Почему монада полезна? Потому что это контейнер, который позволяет цепочечные операции, которые сохраняют семантику. Например,
Optional<?>
сохраняет семантику isPresent дляOptional<String>
,Optional<Integer>
,Optional<MyClass>
и т.д.В качестве грубого примера,
Обратите внимание, что мы начинаем со строки и заканчиваем целым числом. Довольно круто.
В OO это может потребовать небольшого количества ручных помахиваний, но любой метод в Something, который возвращает другой подкласс Something, соответствует критерию функции контейнера, которая возвращает контейнер исходного типа.
Таким образом вы сохраняете семантику - т.е. значение и операции контейнера не меняются, они просто обертывают и улучшают объект внутри контейнера.
источник
Монады в типичном использовании являются функциональным эквивалентом механизмов обработки исключений процедурного программирования.
В современных процедурных языках вы помещаете обработчик исключений вокруг последовательности операторов, любой из которых может вызвать исключение. Если какой-либо из операторов выдает исключение, обычное выполнение последовательности операторов останавливается и передается обработчику исключений.
Функциональные языки программирования, однако, философски избегают функций обработки исключений из-за их «сходной» природы. С точки зрения функционального программирования функции не должны иметь «побочных эффектов», таких как исключения, которые нарушают ход программы.
В действительности побочные эффекты не могут быть исключены в реальном мире, прежде всего, из-за ввода-вывода. Монады в функциональном программировании используются для того, чтобы справиться с этим, принимая набор связанных функций (любой из которых может привести к неожиданному результату) и превращая любой неожиданный результат в инкапсулированные данные, которые все еще могут безопасно проходить через оставшиеся вызовы функций.
Поток управления сохраняется, но неожиданное событие безопасно инкапсулируется и обрабатывается.
источник
Простое объяснение Monads с примером использования Marvel здесь .
Монады - это абстракции, используемые для последовательного выполнения зависимых функций. Эффект здесь означает, что они возвращают тип в форме F [A], например Option [A], где Option - это F, называемый конструктором типов. Давайте посмотрим на это в 2 простых шага
Однако, если функция возвращает тип эффекта, такой как Option [A], то есть A => F [B], композиция не работает, так как для перехода к B нам нужно A => B, но у нас есть A => F [B].
Нам нужен специальный оператор «bind», который знает, как объединить эти функции, которые возвращают F [A].
Функция «связать» определена для конкретного F .
Существует также «возвращение» , типа А => F [A] для любого А , определенный для конкретного F также. Чтобы быть Монадой, F должен иметь эти две функции, определенные для нее.
Таким образом, мы можем построить эффективную функцию A => F [B] из любой чистой функции A => B ,
но данный F может также определять свои собственные непрозрачные «встроенные» специальные функции таких типов, которые пользователь не может определить сам (на чистом языке), например
источник
Я делюсь своим пониманием монад, которые теоретически не могут быть идеальными. Монады о распространении контекста . Монадой является то, что вы определяете некоторый контекст для некоторых данных (или типов данных), а затем определяете, как этот контекст будет переноситься с данными по всему конвейеру обработки. А определение распространения контекста в основном сводится к определению того, как объединить несколько контекстов (одного типа). Использование монад также означает, что эти контексты не будут случайно удалены из данных. С другой стороны, другие данные без контекста могут быть перенесены в новый или существующий контекст. Тогда эта простая концепция может быть использована для обеспечения корректности времени компиляции программы.
источник
Если вы когда-либо использовали Powershell, описанные Эриком шаблоны должны звучать знакомо. Командлеты Powershell - это монады; функциональная композиция представлена конвейером .
Интервью Джеффри Сновера с Эриком Мейером становится более подробным.
источник
Смотри мой ответ "Что такое монада?"
Он начинается с мотивирующего примера, прорабатывает пример, выводит пример монады и формально определяет «монаду».
Он не предполагает никаких знаний о функциональном программировании и использует псевдокод с
function(argument) := expression
синтаксисом с простейшими возможными выражениями.Эта программа на C ++ является реализацией монады псевдокода. (Для справки:
M
является конструктором типа,feed
является операцией «связывания» и операциейwrap
«возврата».)источник
С практической точки зрения (суммируя то, что было сказано во многих предыдущих ответах и связанных статьях), мне кажется, что одна из фундаментальных «целей» (или полезности) монады заключается в использовании зависимостей, неявных в рекурсивных вызовах методов. так называемая композиция функций (то есть когда f1 вызывает f2, вызывает f3, f3 должна быть оценена перед f2 перед f1), чтобы представить последовательную композицию естественным образом, особенно в контексте ленивой модели оценки (то есть последовательная композиция в виде простой последовательности Например, «f3 (); f2 (); f1 ();» в C - трюк особенно очевиден, если вы думаете о случае, когда f3, f2 и f1 фактически ничего не возвращают [их цепочка как f1 (f2 (f3)) является искусственным, чисто предназначенным для создания последовательности]).
Это особенно актуально, когда участвуют побочные эффекты, то есть когда какое-то состояние изменяется (если бы у f1, f2, f3 не было побочных эффектов, не было бы никакого значения, в каком порядке они оцениваются; это большое свойство чистого функциональные языки, чтобы иметь возможность распараллелить эти вычисления, например). Чем больше чистых функций, тем лучше.
Я думаю, с этой узкой точки зрения, монады можно рассматривать как синтаксический сахар для языков, которые предпочитают ленивую оценку (которые оценивают вещи только тогда, когда это абсолютно необходимо, следуя порядку, который не зависит от представления кода), и которые не имеют другие средства представления последовательной композиции. Конечным результатом является то, что фрагменты кода, которые являются «нечистыми» (то есть имеют побочные эффекты), могут быть представлены естественным образом, обязательно, но при этом они четко отделены от чистых функций (без побочных эффектов), которые могут быть оценил лениво.
Это только один аспект, как и здесь .
источник
Самое простое объяснение, которое я могу придумать, заключается в том, что монады - это способ составления функций с впечатляющими результатами (он же состав Клейсли). «Украшенная» функция имеет сигнатуру
a -> (b, smth)
гдеa
иb
являются типами (думаюInt
,Bool
), которые могут отличаться друг от друга, но не обязательно, - иsmth
являются «контекстом» или «приукрашиванием».Этот тип функций также может быть написан,
a -> m b
гдеm
эквивалентно «приукрашивание»smth
. Таким образом, это функции, которые возвращают значения в контексте (например, функции, которые регистрируют свои действия, гдеsmth
находится сообщение регистрации; или функции, которые выполняют ввод \ вывод, и их результаты зависят от результата действия ввода-вывода).Монада - это интерфейс («класс типов»), который заставляет разработчика сообщать ему, как составлять такие функции. Разработчик должен определить функцию композиции
(a -> m b) -> (b -> m c) -> (a -> m c)
для любого типаm
который хочет реализовать интерфейс (это композиция Клейсли).Таким образом, если мы говорим, что у нас есть тип кортежа,
(Int, String)
представляющий результаты вычислений наInt
s, которые также регистрируют свои действия,(_, String)
будучи «украшением» - журналом действия - и две функции,increment :: Int -> (Int, String)
иtwoTimes :: Int -> (Int, String)
мы хотим получить функцию,incrementThenDouble :: Int -> (Int, String)
которая является композицией из двух функций, которая также учитывает журналы.В данном примере монадная реализация двух функций применяется к целочисленному значению 2
incrementThenDouble 2
(которое равноtwoTimes (increment 2)
), которое будет возвращаться(6, " Adding 1. Doubling 3.")
для промежуточных результатов,increment 2
равных(3, " Adding 1.")
иtwoTimes 3
равных(6, " Doubling 3.")
Из этой композиционной функции Клейсли можно получить обычные монадические функции.
источник