Монада (функциональное программирование) - Википедия - Monad (functional programming)

В функциональное программирование, а монада абстракция, позволяющая структурировать программы в целом. Поддерживающие языки могут использовать монады для абстрагирования шаблонный код необходимо по логике программы. Монады достигают этого, предоставляя свои собственные тип данных (особый тип для каждого типа монады), который представляет особую форму вычисление вместе с одним процедура обернуть значения любой базовый тип внутри монады (дающий монадическое значение) и еще один составлять функции которые выводят монадические значения (называемые монадические функции).[1]

Это позволяет монадам упростить широкий спектр задач, например, обработку потенциальных неопределенные значенияМожет быть монада) или сохранение значений в гибком, хорошо сформированном список (с использованием Список монада). С помощью монады программист может превратить сложную последовательность функций в лаконичный трубопровод который отвлекает от управления вспомогательными данными, поток управления, или же побочные эффекты.[1][2]

И понятие монады, и термин первоначально произошли от теория категорий, где монада определяется как функтор с дополнительной структурой.[а] Исследования, начатые в конце 1980-х - начале 1990-х годов, установили, что монады могут объединять, казалось бы, несопоставимые проблемы компьютерных наук в рамках единой функциональной модели. Теория категорий также предусматривает несколько формальных требований, известных как законы монад, что должно быть удовлетворено любой монадой и может быть использовано для проверять монадический код.[3][4]

Поскольку монады делают семантика явные для своего рода вычислений, они также могут использоваться для реализации удобных языковых функций. Некоторые языки, например Haskell, даже предлагают готовые определения в своей основе библиотеки для общей структуры монад и общих экземпляров.[1][5]

Обзор

"Для монады M, значение типа М а представляет доступ к значению типа а в контексте монады ». - К. А. Макканн.[6]

Монада определяется конструктор типов M вместе с двумя операциями:

  • return :: a -> M a (также называемый единица измерения), который обертывает любое значение типа а в монадическое значение типа М а,
  • присоединиться :: M (M a) -> M a (также называемый сочинение), который разворачивает любое обернутое монадическое значение типа М (М а) к простому монадическому значению типа М а.

Конструктор типа M кроме того считается функтор, т.е. обеспечить fmap :: (a -> b) -> M a -> M b, пока возвращаться и присоединиться должны удовлетворять естественным отношениям совместимости, указанным ниже.

На практике монады обычно определяются через их связывать оператор вместо этого, записанный с инфиксной нотацией >>=:

  • bind :: M a -> (a -> M b) -> M b который отображает монадический тип М а к монадическому типу М б задана монадическая функция типа а -> М б.

Обратите внимание, что если связывать или же присоединиться определение монады строго эквивалентно:

  • Картография а -> М б функционально к М а -> М (М б) и присоединение М (М б) к М б , привязка ma >> = f восстанавливается через присоединиться (fmap f ma).
  • Взаимно, давая а = М б , тождественные карты а = М б к М б так что присоединиться к mmb восстанавливается как mmb >> = id, где id - идентификационная карта.

использование

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

В делать нотация в Haskell, например, позволяет писать монадические блоки кода как:

главный :: IO ()главный = делать имя <- getLine                    - getLine :: IO String          putStrLn ("Здравствуй " ++ имя ++ "!")    - putStrLn :: String -> IO ()

в IO монада, приведенное выше эквивалентно привязке getLine >> = (putStrLn. привет) куда привет name = "Привет" ++ name ++ "!". Письмо делать кодовые блоки позволяют нам заимствовать читаемость императивного кода. С помощью связывать однако поощряет писать чистые конвейеры функций.

Хотя последовательности операций кажутся наиболее мощным приложением монад в информатике, при обработке состояний, побочных эффектов и операций ввода-вывода эта концепция гораздо более общая, и существует множество более простых примеров, приведенных, например, Список и Может быть монады. Более тонкий Читатель монада, описывающая все типы функций с -> а из общего источника s, является еще одним каноническим примером и структурной моделью для многих других монад, таких как Состояние и IO.

Монада списка

Списки представляют собой первый пример монады. Следовательно, несмотря на их абстрактное определение, монады воплощаются в очень простом типе данных, общем для большинства языков программирования.

С [a] = Список обозначающий тип списков с элементами типа а, единица и состав в Списокопределяются:

  • unit :: a -> [a] присваивает любое значение Икс типа а одноэлементный список [Икс] содержащий этот элемент,
  • join :: [[a]] -> [a] объединяет список списков типа [[а]] объединив их в плоский список типа [а].

Благодаря своей большой простоте монада списков в некотором роде универсальна: она может представлять любую последовательность термов, ожидающих составления ассоциативной операцией.

Рассмотрим, например, функцию труба: [b -> b] -> b -> b, составляя список функций, рассматриваемых как конвейер. Если рассматривать вложенные списки как метафору порожденных подпроцессов, есть два эквивалентных способа сформировать результирующий связанный процесс:

  • трубка . (карта конвейера) :: [[b -> b]] -> b -> b
  • трубка . join :: [[b -> b]] -> b -> b

Эта эквивалентность отражает ассоциативность из (.): скобки можно опустить (е. ж). час = f. (г. ч) = f. грамм . час, так как порядок, в котором составляются функции, не имеет значения. Работа с Список монада позволяет полностью распознать это свойство и сосредоточиться на обработке только значений монадического типа Список (b -> b), к которым вложенные списки естественным образом сводятся монадической композицией.

Обратите внимание, что арифметические операции, такие как сумма :: [Num] -> Num и prod :: [Num] -> Num также предоставьте простые варианты приведенного выше примера. Это следствие того, что (Num, (+), 0), (Num, (*), 1), и (b -> b, (.), id) все общие примеры моноиды, т.е.пространства, в которых определен ассоциативный закон композиции и есть единица измерения. Об этих простых структурах следует помнить, поскольку монады сами по себе являются абстракцией моноидной концепции, и, как бы эзотерически она ни была монада - это моноид в категории эндофункторов.

Еще одно распространенное толкование Список монада как представление альтернативы, просмотр [а] как совокупность возможных результатов для значения, выбранного в а. Хотя это менее естественно, это дает удобную точку зрения на связывать оператор, который просто объединяет списки, полученные из поэлементного применения функции:

  • bind :: [a] -> (a -> [b]) -> [b]

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

Рассмотрим, например, следующее powerset функция, возвращающая все подсписки данного списка:

powerset :: [а] -> [[а]]powerset [] = [[]]powerset (Икс:хз) = делать            ys <- powerset хз       - ys :: [a]    [Икс:ys, ys]              - либо поставить x, либо нет

Его делать реализация блока, эквивалентная powerset (x: xs) = powerset xs >> = ( ys -> [x: ys, ys]), иллюстрирует выразительностьсвязывать.

Пример: Может быть

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

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

данные Может быть Т  =  Только Т или же Ничего

Может Т можно понимать как "оборачивающий" тип, оборачивающий тип Т в новый тип со встроенной обработкой исключений, но без информации о причине исключения.

В следующем коде переменные с префиксом м иметь тип Может Т для какого-то типа Т. Например, если переменная mx содержит значение, это Просто х, где переменная Икс имеет тип Т. λx → ... является анонимная функция с параметром Икс чей тип предполагаемый, и это функциональная композиция оператор.

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

Функция сложения Добавить, что делает именно это при добавлении двух Может быть значения, mx и мой, можно определить так:

 Добавить : (Может быть Число, Может быть Число)  Может быть Число Добавить(mx,мой) = ...     если mx является Ничего тогда         ... Ничего     еще если мой является Ничего тогда         ... Ничего     еще         ... Только (Икс + у)     конец если

Написание функций, которые обрабатывают Может быть Однако индивидуальные значения могут быть утомительными и станут еще больше, когда будет определено больше функций. Операция по объединению шагов - один из способов облегчить это, и инфиксный оператор подобно х >> = у, он может даже интуитивно представить передачу (возможно, неопределенного) результата каждого шага в следующий. Поскольку каждый результат технически вставлен в другой функцияоднако оператор примет функцию в качестве параметра. В качестве Добавить уже указывает тип своего выходного значения, это не должно помешать сохранить гибкость оператора и принять функции, которые выводят разные типы из своего ввода:

 >>= : (Может быть Т, Т  Может быть U)  Может быть U (mx >>= ж) = ...     если mx является (Только Икс) тогда         ... ж(Икс)    - f возвращает определенное значение типа Maybe U     еще         ... Ничего - f не возвращает значения     конец если

С >>= имеется в наличии, Добавить теперь можно переопределить как нечто гораздо более компактное:

 Добавить(mx,мой)  =  mx >>= λx -> (мой >>= λy -> Только (Икс + у))

Это более кратко, но небольшой дополнительный анализ открывает нечто еще более мощное. Во-первых, единственная роль, которая Только играет в Добавить заключается в том, чтобы пометить базовое значение как также Может быть ценить. Чтобы подчеркнуть, как Только действует на базовое значение, обернув его, его также можно переопределить как функцию, называемую эта сейчас:

 эта : Т  Может быть Т эта(Икс)  =  Только Икс

Общая картина состоит в том, что эти две функции >>= и эта были разработаны, чтобы упростить Добавить, но они явно не зависят от специфики Добавить в любой путь, просто Может быть Эти функции могут применяться к любым значениям и функциям Может быть тип, независимо от типов базовых значений. Например, вот краткий НЕТ оператор из (Клини) тройная логика который использует те же функции для автоматизации неопределенных значений:

тринот : Может быть Булево  Может быть Булевотринот(mp)  =  mp >>= λp -> (эта  нет) п

Оказывается Может быть типа вместе с >>= и эта, образует монаду. В то время как другие монады будут воплощать различные логические процессы, а некоторые могут иметь дополнительные свойства, все они будут иметь три одинаковых компонента (прямо или косвенно), которые следуют основной схеме этого примера.[1][7]

Определение

Более общее определение монады в функциональном программировании, используемое в приведенном выше примере, фактически основано на Клейсли трехместный а не стандартное определение теории категорий. Однако две конструкции оказываются математически эквивалентными, так что любое определение даст действительную монаду. Учитывая любые четко определенные базовые типы Т, U, монада состоит из трех частей:

  • А конструктор типов M что создает монадический тип M T[b]
  • А преобразователь типов, часто называют единица измерения или же возвращаться, который встраивает объект Икс в монаде:
    единица (x): T → M T[c]
  • А комбинатор, обычно называемый связывать (как в привязка переменной ) и представлен инфиксный оператор >>=, который разворачивает монадическую переменную, а затем вставляет ее в монадическую функцию / выражение, в результате чего получается новое монадическое значение:
    (mx >> = f): (M T, T → M U) → M U[d]

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

  • единица измерения это левая идентичность за связывать:
    единица (a) >> = λx → f (x) f (а)
  • единица измерения также является право-тождеством для связывать:
    ma >> = λx → единица (x) ма
  • связывать по существу ассоциативный:[e]
    ma >> = λx → (f (x) >> = λy → g (y)) (ma >> = λx → f (x)) >> = λy → g (y)[1]

Алгебраически это означает, что каждая монада порождает категорию (называемую Категория Клейсли ) и а моноид в категории функторов (от значений к вычислениям) с монадической композицией в качестве бинарного оператора и единица измерения как личность.[нужна цитата ]

использование

Ценность шаблона монады выходит за рамки простого уплотнения кода и предоставления ссылки на математические рассуждения. парадигма программирования разработчик использует, следуя шаблону монад, приносит много преимуществ чисто функциональное программированиематериализация особый вид вычислений, не только монада инкапсулирует утомительные детали этого вычислительного шаблона, но он делает это в декларативный способ, улучшающий ясность кода. Поскольку монадические значения явно представляют не только вычисленные значения, но и вычисленные последствия, монадическое выражение можно заменить его значением в ссылочно прозрачные позиции, как и чистые выражения, позволяющие использовать множество методов и оптимизаций, основанных на переписывание.[4]

Обычно программисты используют связывать чтобы связать монадические функции в последовательность, что привело к тому, что некоторые описали монады как «программируемые точки с запятой», указание на то, сколько императив языки используют точку с запятой для разделения заявления.[1][5]Однако следует подчеркнуть, что монады на самом деле не упорядочивают вычисления; даже в языках, которые используют их как центральные функции, более простая композиция функций может упорядочивать шаги в программе. Общая полезность монады заключается скорее в упрощении структуры программы и улучшении разделение проблем через абстракцию.[4][9]

Структуру монады также можно рассматривать как однозначно математическую и время компиляции вариация на декоратор шаблон Некоторые монады могут передавать дополнительные данные, недоступные функциям, а некоторые даже обеспечивают более тонкий контроль над выполнением, например, вызывая функцию только при определенных условиях, поскольку они позволяют программистам приложений реализовать логика предметной области при разгрузке шаблонного кода на предварительно разработанные модули монады можно даже рассматривать как инструмент для аспектно-ориентированное программирование.[10]

Еще одно заслуживающее внимания использование монад - изоляция побочных эффектов, таких как ввод, вывод или изменчивый государственный, в остальном чисто функциональный код. Даже чисто функциональные языки может по-прежнему реализовывать эти "нечистые" вычисления без монад, за счет сложной смеси композиции функций и стиль передачи (CPS) в частности.[2]С помощью монад, однако, большая часть этого каркаса может быть абстрагирована, по существу, если взять каждый повторяющийся шаблон в коде CPS и объединить его в отдельную монаду.[4]

Если язык не поддерживает монады по умолчанию, все еще можно реализовать шаблон, часто без особых трудностей. При переводе из теории категорий в термины программирования структура монады представляет собой общая концепция и может быть определен непосредственно на любом языке, который поддерживает эквивалентную функцию для ограниченный полиморфизм Способность концепта оставаться агностическим в отношении операционных деталей при работе с базовыми типами - это мощно, но уникальные особенности и строгое поведение монад выделяют их среди других концепций.[11]

Приложения

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

Вот лишь несколько приложений, в основе дизайна которых лежат монады:

История

Термин «монада» в программировании восходит к APL и J языки программирования, которые имеют тенденцию быть чисто функциональными. Однако в этих языках «монада» - это только сокращение для функции, принимающей один параметр (функция с двумя параметрами, являющаяся «диадой», и так далее).[17]

Математик Роджер Годеман был первым, кто сформулировал концепцию монады (назвав ее «стандартной конструкцией») в конце 1950-х годов, хотя термин «монада», который стал доминирующим, появился благодаря теоретикам категорий Saunders Mac Lane.[нужна цитата ] Форма, определенная выше с использованием связыватьОднако впервые был описан в 1965 году математиком Генрих Клейсли чтобы доказать, что любую монаду можно охарактеризовать как примыкание между двумя (ковариантными) функторами.[18]

Начиная с 1980-х гг., В сообществе информатики начало появляться расплывчатое представление о шаблоне монад. Филип Вадлер, специалист в области информатики Джон С. Рейнольдс предвидел несколько аспектов этого в 1970-х и начале 1980-х, когда обсуждал ценность стиль передачи теория категорий как богатый источник формальной семантики и различие типов между значениями и вычислениями.[4]Язык исследования Опал, который активно разрабатывался до 1990 года, также эффективно основывал ввод-вывод на монадическом типе, но в то время соединение не было реализовано.[19]

Компьютерный ученый Эухенио Моджи был первым, кто явно связал монаду теории категорий с функциональным программированием в документе конференции 1989 года,[20] затем в 1991 г. последовали более изысканные публикации в журнале. В более ранней работе несколько компьютерных ученых продвинулись вперед, используя теорию категорий, чтобы обеспечить семантику для лямбда-исчисление. Ключевой вывод Моджи заключался в том, что реальная программа - это не просто функция от значений к другим значениям, а скорее преобразование, которое формирует вычисления по этим ценностям. При формализации в терминах теории категорий это приводит к выводу, что монады являются структурой для представления этих вычислений.[3]

Несколько других популяризировали и построили эту идею, в том числе Филип Вадлер и Саймон Пейтон Джонс, оба из которых были вовлечены в спецификацию Haskell. В частности, Haskell использовал проблемную модель «ленивого потока» вплоть до версии 1.2 для согласования ввода-вывода с ленивая оценка, пока не переключимся на более гибкий монадический интерфейс.[21] Сообщество Haskell продолжит применять монады ко многим задачам функционального программирования, а исследователи, работающие с Haskell, в конечном итоге обобщили шаблон монады на более широкую иерархию структур, включая аппликативные функторы и стрелки.[нужна цитата ]

Сначала программирование с помощью монад в основном ограничивалось Haskell и его производными, но, поскольку функциональное программирование повлияло на другие парадигмы, многие языки включили шаблон монады (по духу, если не по названию). Составы теперь существуют в Схема, Perl, Python, Ракетка, Clojure, Scala, F #, а также были рассмотрены для нового ML стандарт.[нужна цитата ]

Анализ

Одним из преимуществ монадного шаблона является обеспечение математической точности логики программы. Законы монад могут использоваться не только для проверки достоверности экземпляра, но и функции из связанных структур (например, функторов) могут использоваться через подтип.

Проверка законов монад

Возвращаясь к Может быть Например, было объявлено, что его компоненты составляют монаду, но не было дано никаких доказательств того, что она удовлетворяет законам монад.

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

Закон 1:  eta (a) >> = f (x) ⇔ (Just a) >> = f (x) ⇔ f (a)
Закон 2:  ma >> = eta (x) ⇔ ma если ма является (Просто) тогда            eta (a) ⇔ Просто еще                        или же            Ничего ⇔ Ничего конец, если
Закон 3.  (ma >> = f (x)) >> = g (y) ⇔ ma >> = (f (x) >> = g (y))        если (ma >> = f (x)) является (Просто б) тогда               если ма является (Просто) тогда            g (ma >> = f (x)) (f (x) >> = g (y)) а еще                                            еще            Ничего ничего конец, если                                          конец, еслиесли ма является (Просто) и f (а) является (Просто б) тогда                             (g ∘ f) а иначе если ма является (Просто) и f (а) тогда ничего                       Ничего еще                       Ничего конец, если

Происхождение от функторы

Хотя в информатике это реже, можно напрямую использовать теорию категорий, которая определяет монаду как функтор с двумя дополнительными естественные преобразования. Итак, для начала структура требует функция высшего порядка (или "функциональный") с именем карта квалифицироваться как функтор:

отображение φ: (a → b) → (ma → mb)

Однако это не всегда является серьезной проблемой, особенно когда монада является производной от уже существующего функтора, после чего монада наследует карта автоматически. (По историческим причинам это карта вместо этого называется fmap в Haskell.)

Первое преобразование монады на самом деле такое же единица измерения из тройки Клейсли, но внимательно следуя иерархии структур, получается единица измерения характеризует аппликативный функтор, промежуточная структура между монадой и базовым функтором. В аппликативном контексте единица измерения иногда упоминается как чистый но это все та же функция. Чем отличается эта конструкция, так это закон единица измерения должен удовлетворять; в качестве связывать не определено, ограничение задается в терминах карта вместо:

(единица ∘ φ) x ↔ ((map φ) ∘ unit) x[22]

Последний скачок от аппликативного функтора к монаде происходит со вторым преобразованием, присоединиться функция (в теории категорий это естественное преобразование, обычно называемое μ), который «сглаживает» вложенные приложения монады:

присоединиться (ММА): M (M T) → M T

В качестве характеристической функции присоединиться также должны удовлетворять трем вариациям законов монад:[нужна цитата ]

присоединиться ∘ (присоединиться к карте) mmma ↔ (присоединиться ∘ присоединиться) mmma ↔ ma
соединение ∘ (блок карты) ma ↔ (соединение ∘ блок) ma ↔ ma
join ∘ (карта карта φ) mma ↔ ((карта φ) ∘ join) mma ↔ mb

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

(отображение φ) ma ↔ ma >> = (единица ∘ φ)
присоединиться (mma) ↔ mma >> = id
ma >> = f ↔ (join ∘ (карта f)) ma[23]

Другой пример: Список

В сложный многозначный квадрат и куб корень функции могут быть составлен поэтому они производят шестую корневую функцию. Структура, которая управляет типом ввода и вывода, и структура, составляющая различные действия, вместе составляют список монада.[24]
В символ пули • обозначает связывать оператор z комплексное число, квадратные скобки обозначают множество, а: = означает 'определяется как ':
(жграмм)(z) := добавить(карта(ж,грамм(z)))поднимать(ж) = ж° := единица измеренияж = жединица измеренияsqrt°(z) == добавить(карта(единица измерения,sqrt(z)))= добавить(карта(sqrt,единица измерения(z)))sxrt(z) = (cbrt°•sqrt°)(z) == добавить(карта(cbrt°,sqrt°(z)))
[сомнительный ]

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

Встраивание простого значения в список также тривиально для большинства языков:

unit (x) = [x]

Отсюда итеративное применение функции с понимание списка может показаться легким выбором для связывать и преобразование списков в полную монаду. Сложность этого подхода состоит в том, что связывать ожидает монадических функций, которые в этом случае будут выводить сами списки; по мере применения большего количества функций слои вложенных списков будут накапливаться, что требует большего, чем базовое понимание.

Однако процедура применения любых просто работать со всем списком, другими словами карта, прямолинейно:

(отображение φ) xlist = [φ (x1), φ (x2), ..., φ (xn)]

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

join (xlistlist) = join ([xlist1, xlist2, ..., xlistn]) = xlist1 ++ xlist2 ++ ... ++ xlistn

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

(xlist >> = f) = присоединиться ∘ (карта f) xlist

Одно приложение для этого монадического списка представляет недетерминированное вычисление.Список может хранить результаты для всех путей выполнения в алгоритме, а затем уплотнять себя на каждом шаге, чтобы «забыть», какие пути привели к каким результатам (иногда важное отличие от детерминированных исчерпывающих алгоритмов). Еще одно преимущество заключается в том, что проверки могут быть встроены в монаду ; определенные пути могут быть прозрачно сокращены в их первой точке отказа, без необходимости переписывать функции в конвейере.[23]

Вторая ситуация, когда Список сияет сочиняет многозначные функции. Например, пth сложный корень числа должны уступить п различные комплексные числа, но если другое мЗатем из этих результатов извлекается корень, последний м • п значения должны быть идентичны выходным данным м • пй корень.Список полностью автоматизирует эту проблему, сводя результаты каждого шага в плоский, математически правильный список.[24]

Методы

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

Синтаксический сахар нотация

Хотя использование связывать открыто часто имеет смысл, многие программисты предпочитают синтаксис, имитирующий императивные инструкции (называемые нотация в Haskell, обозначение исполнения в OCaml, вычислительные выражения в F #,[25] и для понимания в Scala ). Это только синтаксический сахар который маскирует монадический конвейер под блок кода; затем компилятор незаметно переведет эти выражения в базовый функциональный код.

Перевод Добавить функция от Может быть в Haskell может показать эту функцию в действии. Немонадическая версия Добавить в Haskell выглядит так:

Добавить mx мой =    дело mx из        Ничего -> Ничего        Только Икс  -> дело мой из                       Ничего -> Ничего                       Только у  -> Только (Икс + у)

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

Добавить mx мой =    mx >>= (\Икс ->        мой >>= (\у ->            возвращаться (Икс + у)))

Тем не менее, с помощью do-notation это может быть переработано еще дальше в очень интуитивную последовательность:

Добавить mx мой = делать    Икс <- mx    у <- мой    возвращаться (Икс + у)

Второй пример показывает, как Может быть может использоваться на совершенно другом языке: F #. С вычислительными выражениями функция «безопасного деления», которая возвращает Никто для неопределенного операнда или же деление на ноль можно записать как:

позволять readNum () =  позволять s = Консоль.ReadLine()  позволять succ,v = Int32.TryParse(s)  если (succ) тогда Немного(v) еще Никтопозволять secure_div =   может быть {     позволять! Икс = readNum()    позволять! у = readNum()    если (у = 0)     тогда Никто    еще возвращаться (Икс / у)  }

Во время сборки компилятор внутренне «обезвожит» эту функцию в более плотную цепочку связывать звонки:

может быть.Задерживать(весело () ->  может быть.Связывать(readNum(), весело Икс ->    может быть.Связывать(readNum(), весело у ->      если (у=0) тогда Никто еще может быть.Возвращаться(Икс / у))))

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

делать { Икс <- возвращаться v; ж Икс }            ==  делать { ж v }делать { Икс <- м; возвращаться Икс }              ==  делать { м }делать { у <- делать { Икс <- м; ж Икс }; грамм у }  ==  делать { Икс <- м; у <- ж Икс; грамм у }

Хотя это удобно, разработчику всегда следует помнить, что этот стиль блока является чисто синтаксическим и может быть заменен внешне монадическими (или даже немонадическими) выражениями CPS. связывать Для выражения монадического конвейера во многих случаях все еще может быть яснее, и некоторые сторонники функционального программирования даже утверждают, что, поскольку блочный стиль позволяет новичкам переносить привычки из императивного программирования, его следует избегать по умолчанию и использовать только тогда, когда он явно лучше.[26][1]

Общий интерфейс

Каждой монаде нужна конкретная реализация, которая соответствует законам монад, но другие аспекты, такие как отношение к другим структурам или стандартные идиомы в пределах языка, являются общими для всех монад. В результате язык или библиотека могут предоставлять общие Монада интерфейс с прототипы функций, отношения подтипов и другие общие факты. Помимо обеспечения старта для разработки и гарантии того, что новая монада наследует особенности супертипа (например, функторы), проверка дизайна монады по интерфейсу добавляет еще один уровень контроля качества.[нужна цитата ]

Операторы

Монадический код часто можно еще больше упростить за счет разумного использования операторов. карта функционал может быть особенно полезным, поскольку он работает не только с одноранговыми монадическими функциями; пока монадическая функция должна работать аналогично предопределенному оператору, карта можно использовать для мгновенного "поднимать "более простой оператор в монадический.[f]С помощью этой техники определение Добавить от Может быть пример можно разделить на:

добавить (mx, my) = карта (+)

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

добавить: (Число монады, Число монады) → Число монады[27]

Другой монадический оператор, который также полезен для анализа, - это монадическая композиция (представленная как infix >=> здесь), что позволяет связывать монадические функции в более математическом стиле:

(f> => g) x = (f (x) → mb) >> = g (y = b)

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

(единица> => g) ↔ g (f> => единица) ↔ f (f> => g)> => h ↔ f> => (g> => h)[1]

Вариации

На математическом уровне некоторые монады обладают особенно хорошими свойствами и однозначно подходят для определенных задач.

Аддитивные монады

An аддитивная монада - монада, наделенная дополнительным замкнутым ассоциативным бинарным оператором mplus и элемент идентичности под mplus, называется mzero. Может быть монаду можно считать аддитивной, с Ничего в качестве mzero и вариация на ИЛИ ЖЕ оператор как mplus.Список также аддитивная монада с пустым списком [] действуя как mzero и оператор конкатенации ++ в качестве mplus.

Интуитивно mzero представляет собой монадическую оболочку без значения из базового типа, но также считается «нулем» (а не «единицей»), поскольку действует как поглотитель за связывать, возвращаясь mzero всякий раз, когда он привязан к монадической функции. Это свойство двустороннее, и связывать также вернется mzero когда любое значение связано с монадическим нулевая функция.

В терминах теории категорий аддитивная монада квалифицируется как моноид над монадическими функциями с связывать (как и все монады), и снова над монадическими значениями через mplus.[28][грамм]

Бесплатные монады

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

Например, работая полностью через Только и Ничего маркеры, Может быть монада на самом деле свободная монада. Список монада, с другой стороны, не является бесплатной монадой, так как она приносит дополнительные, конкретные факты о списках (например, добавить) в его определение. Последний пример - абстрактная свободная монада, использующая маркеры типа для единица измерения и связывать и рекурсивный конструктор линейного типа:

Новый тип Свободный F (T) = Единица T или же Bind (F, Free F (T)) unit (x) = Unit xmx >> = f = ... если mx является Блок x тогда        ... f (x) еще        ... Привязать (f, mx) конец, если

Однако свободные монады нет ограничен связным списком, как в этом примере, и может быть построен вокруг других структур, таких как деревья.

Намеренное использование свободных монад поначалу может показаться непрактичным, но их формальная природа особенно хорошо подходит для синтаксических проблем. Свободную монаду можно использовать для отслеживания синтаксиса и типа, оставляя семантику на потом, и она нашла применение в парсерах и переводчики как результат.[29]Другие применяли их и к более динамичным, оперативным задачам, таким как обеспечение итераторы в пределах языка.[30]

Комонады

Помимо создания монад с дополнительными свойствами, для любой данной монады также можно определить комонада.Концептуально, если монады представляют вычисления, построенные из базовых значений, тогда комонады можно рассматривать как редукции обратно к значениям. Монадический код, в некотором смысле, не может быть полностью «распакован»; после того, как значение заключено в монаду, оно остается там в карантине вместе с любыми побочными эффектами (что хорошо в чисто функциональном программировании). Однако иногда проблема больше связана с потреблением контекстных данных, которые комонады могут моделировать явно.

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

  • Конструктор типа W который отмечает тип высшего порядка Вт Т
  • Двойной единица измерения, называется графство здесь извлекает базовое значение из комонады:
счет (ва): W T → T
  • Разворот связывать (также представлен =>>) который продлеватьs цепочка редуцирующих функций:
(wa = >> f): (W U, W U → T) → W T[час]

продлевать и графство также должны удовлетворять двойникам монадных законов:

countit ∘ ( (wa = >> f) → wb )  ↔ f (wa) → bwa = >> коит ↔ wawa ( (= >> f (wx = wa)) → wb (= >> g (wy = wb)) → wc )( ва (= >> f (wx = wa)) → wb ) (= >> g (wy = wb)) → wc

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

  • дублировать берет уже комонадическое значение и оборачивает его другим слоем комонадической структуры:
дубликат (wa): W T → W (W T)

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

((дубликат карты) ∘ дубликат) ва ↔ (дубликат ∘ дубликат) ва ↔ wwwa ((счетчик карты) ∘ дубликат) ва ↔ (счетчик ∘ дубликат) ва ↔ ва ((карта карты φ) ∘ дубликат) ва ↔ (дубликат ∘ (карта φ)) wa ↔ wwb

Как и в случае с монадами, две формы могут быть преобразованы автоматически:

(отображение φ) wa ↔ wa = >> (φ ∘ counit) wxдупликация wa ↔ wa = >> wx
wa = >> f (wx) ↔ ((карта f) ∘ дублировать) wa

Простой пример - Комонад продукта, который выводит значения на основе входного значения и общих данных среды. Товар comonad - это просто двойник Писатель монада и фактически то же самое, что и Читатель монада (обе обсуждаются ниже).Товар и Читатель различаются только сигнатурами функций, которые они принимают, и тем, как они дополняют эти функции путем обертывания или разворачивания значений.

Менее тривиальный пример - Комонада потока, который можно использовать для представления потоки данных и прикрепить фильтры к входящим сигналам с продлеватьНа самом деле, хотя и не так популярны, как монады, исследователи обнаружили, что комонады особенно полезны для потоковая обработка и моделирование программирование потока данных.[31][32]

Однако из-за их строгих определений нельзя просто перемещать объекты между монадами и комонадами. В качестве еще более высокой абстракции стрелки может включать в себя обе структуры, но поиск более детальных способов комбинирования монадического и комонадического кода является активной областью исследований.[33][34]

Еще примеры

Монада идентичности

Простейшая монада - это Монада идентичности, который просто аннотирует простые значения и функции, чтобы удовлетворить законам монад:

Новый тип Id T = Tunit (x) = x (x >> = f) = f (x)

Личность действительно имеет допустимое применение, например, предоставление базовый вариант для рекурсивного монада трансформеры Его также можно использовать для выполнения базового присваивания переменных в блоке императивного стиля.[я][нужна цитата ]

Коллекции

Любая коллекция с правильным добавить уже свободный моноид, но оказывается, что Список не единственный коллекция который также имеет четко определенный присоединиться и квалифицируется как монада. Можно даже видоизменить Список в эти другие монадические коллекции, просто наложив специальные свойства на добавить:[j][нужна цитата ]

КоллекцияСвойства моноида
СписокСвободный
Конечный мультимножествоКоммутативный
Конечный наборКоммутативный и идемпотент
Конечный перестановкиНекоммутативный и идемпотентный

Монада ввода-вывода (Haskell)

Как уже упоминалось, чистый код не должен иметь неуправляемых побочных эффектов, но это не мешает программе явно описание и управление эффектами. Эта идея является центральной в Haskell. IO монада, где объект типа IO a можно рассматривать как содержащий текущее состояние мира за пределами программы и вычисляющий значение типа а. Вычисление, которое не вычисляет никакого значения, то есть процедура, имеет тип IO (), "вычисляя" фиктивное значение ().Когда программист связывает IO значение функции, функция принимает решения на основе этого взгляда на мир (ввод от пользователей, файлов и т. д.), затем выдает монадическое значение, отражающее новое состояние мира (вывод программы).[21]

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

doesFileExist :: Путь файла -> IO Boolудалить файл :: Путь файла -> IO ()

Первый интересуется, действительно ли существует данный файл, и в результате выводит Логическое значение в пределах IO с другой стороны, вторая функция занимается только работой с файловой системой, поэтому IO контейнер, который он выводит, пуст.

IO однако не ограничивается только файловым вводом-выводом; он даже позволяет пользователю ввод-вывод и, наряду с императивным синтаксическим сахаром, может имитировать типичный "Привет, мир!" программа:

главный :: IO ()главный = делать  putStrLn "Привет, мир!"  putStrLn "Как вас зовут, пользователь?"  имя <- getLine  putStrLn ("Рад встрече, " ++ имя ++ "!")

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

главный :: IO ()главный =  putStrLn "Привет, мир!" >>  putStrLn "Как вас зовут, пользователь?" >>   getLine >>= (\имя ->    putStrLn ("Рад встрече, " ++ имя ++ "!"))

Монада писателя (JavaScript)

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

Чтобы показать, что шаблон монада не ограничивается преимущественно функциональными языками, в этом примере реализуется Писатель монада в JavaScript. Во-первых, массив (с вложенными хвостами) позволяет построить Писатель тип как связанный список Базовое выходное значение будет находиться в позиции 0 массива, а позиция 1 неявно будет содержать цепочку вспомогательных заметок:

const писатель = [ценить, []];

Определение единица измерения тоже очень просто:

const единица измерения = ценить => [ценить, []];

Только единица измерения необходим для определения простых функций, которые выводят Писатель объекты с отладочными примечаниями:

const в квадрате = Икс => [Икс * Икс, [`${Икс} был в квадрате.]];const вдвое меньше = Икс => [Икс / 2, [`${Икс} был уменьшен вдвое.]];

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

const связывать = (писатель, преобразовать) => {    const [ценить, бревно] = писатель;    const [результат, обновления] = преобразовать(ценить);    возвращаться [результат, бревно.concat(обновления)];};

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

const трубопровод = (писатель, ...трансформирует) =>    трансформирует.уменьшать(связывать, писатель);

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

трубопровод(единица измерения(4), в квадрате, вдвое меньше);// Полученный объект записи = [8, ['4 возведено в квадрат.', '16 было уменьшено вдвое. ']]

Монада окружающей среды

Монада среды (также называемая читательская монада и функция монада) позволяет вычислению зависеть от значений из общей среды. Конструктор типа монады отображает тип Т функциям типа EТ, куда E это тип разделяемой среды. Функции монады:

Полезны следующие монадические операции:

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

Формально значение в монаде среды эквивалентно функции с дополнительным анонимным аргументом; возвращаться и связывать эквивалентны K и S комбинаторы соответственно в Расчет комбинатора SKI.

Государственные монады

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

тип Состояние s т = s -> (т, s)

Обратите внимание, что эта монада принимает параметр типа, тип информации о состоянии. Операции с монадой определяются следующим образом:

- «return» производит заданное значение без изменения состояния.возвращаться Икс = \s -> (Икс, s)- "bind" изменяет m так, что он применяет f к своему результату.м >>= ж = \р -> позволять (Икс, s) = м р в (ж Икс) s

К полезным операциям с состоянием относятся:

получать = \s -> (s, s) - Изучите состояние на этом этапе вычислений.положить s = \_ -> ((), s) - Заменить состояние.модифицировать ж = \s -> ((), ж s) - Обновить состояние

Другая операция применяет монаду состояния к данному начальному состоянию:

runState :: Состояние s а -> s -> (а, s)runState т s = т s

do-блоки в монаде состояний - это последовательности операций, которые могут проверять и обновлять данные состояния.

Неформально государственная монада государственного типа S отображает тип возвращаемых значений Т на функции типа , куда S это основное состояние. В возвращаться и связывать функции:

.

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

Монада продолжения

А продолжение монада с возвращаемым типом р тип карты Т на функции типа . Используется для моделирования стиль передачи. Функции возврата и связывания следующие:

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

Журнал программы

Следующий код - псевдокод. Предположим, у нас есть две функции фу и бар, с типами

фу : int -> intбар : int -> int

То есть обе функции принимают целое число и возвращают другое целое число. Затем мы можем последовательно применять функции следующим образом:

фу (бар Икс)

Где результат - результат фу применяется к результату бар применительно к Икс.

Но предположим, что мы отлаживаем нашу программу и хотим добавить сообщения журнала в фу и бар.Изменяем типы так:

фу : int -> int * нитьбар : int -> int * нить

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

К сожалению, это означает, что мы больше не можем составлять фу и бар, как их тип ввода int не совместим с их типом вывода int * строка. И хотя мы снова можем получить совместимость, изменив типы каждой функции, чтобы int * строка -> int * строка, это потребовало бы, чтобы мы добавили шаблонный код к каждой функции для извлечения целого числа из кортежа, что стало бы утомительно по мере увеличения числа таких функций.

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

связывать : int * нить -> (int -> int * нить) -> int * нить

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

Теперь мы восстановили некоторую композицию. Например:

связывать (связывать (Икс,s) бар) фу

Где (х, с) представляет собой целочисленный и строковый кортеж.

Чтобы сделать преимущества еще более очевидными, давайте определим инфиксный оператор как псевдоним для связывать:

(>>=) : int * нить -> (int -> int * нить) -> int * нить

Так что t >> = f такой же как привязать t f.

Тогда приведенный выше пример станет:

((Икс,s) >>= бар) >>= фу

Наконец, было бы неплохо не писать (Икс, "") каждый раз, когда мы хотим создать пустое сообщение журнала, где "" - это пустая строка, поэтому давайте определим новую функцию:

возвращаться : int -> int * нить

Какие обертывания Икс в кортеже, описанном выше.

Теперь у нас есть хороший конвейер для регистрации сообщений:

((возвращаться Икс) >>= бар) >>= фу

Это позволяет нам более легко регистрировать эффекты бар и фу на Икс.

int * строка аналогичен монадическое значение. связывать и возвращаться аналогичны соответствующим одноименным функциям. int * строка, связывать, и возвращаться образуют монаду.

Смотрите также

Альтернативы для моделирования вычислений:

Связанные концепции дизайна:

  • Аспектно-ориентированное программирование подчеркивает необходимость разделения вспомогательного бухгалтерского кода для повышения модульности и простоты
  • Инверсия контроля это абстрактный принцип вызова определенных функций из всеобъемлющей структуры
  • Типовые классы это особая языковая функция, используемая для реализации монад и других структур в Haskell
  • В декоратор шаблон это более конкретный способ достижения аналогичных преимуществ в объектно-ориентированном программировании.

Обобщения монад:

  • Аппликативные функторы обобщить из монад, оставив только единица измерения и законы, относящиеся к карта
  • Стрелки использовать дополнительную структуру для объединения простых функций и монад в единый интерфейс
  • Трансформеры Monad воздействовать на отдельные монады, чтобы объединить их модульно

Примечания

  1. ^ В связи с тем, что функционирует на нескольких свободные переменные распространены в программировании, монады, описанные в этой статье, технически являются той категорией, которую теоретики назвали бы сильные монады.[3]
  2. ^ Семантически M не является тривиальным и представляет собой эндофунктор над категория всех хорошо типизированных значений:
  3. ^ В то время как (параметрически полиморфная) функция в терминах программирования, единица измерения (часто называют η в теории категорий) математически естественная трансформация, который отображается между функторы:
  4. ^ связывать, с другой стороны, не является естественным преобразованием теории категорий, а скорее расширением который лифты отображение (от значений к вычислениям) в морфизм между вычислениями:
  5. ^ Строго говоря, связывать не может быть формально ассоциативным во всех контекстах, поскольку соответствует приложению в лямбда-исчисление а не математика. В строгом лямбда-исчислении оценка связывать может потребоваться сначала обернуть правильный термин (при связывании двух монадических значений) или саму привязку (между двумя монадическими функциями) в анонимная функция чтобы по-прежнему принимать ввод слева.[8]
  6. ^ Некоторые языки, такие как Haskell, даже предоставляют псевдоним для карта в других контекстах называется подниматьнаряду с несколькими версиями для разного количества параметров, здесь эта деталь игнорируется.
  7. ^ Алгебраически отношения между двумя (некоммутативными) моноидными аспектами напоминают отношения почти полукольцо, и некоторые аддитивные монады действительно квалифицируются как таковые. Однако не все аддитивные монады соответствуют распределительный законы даже почти полукольца.[28]
  8. ^ В Haskell, продлевать фактически определяется с заменой входов, но поскольку каррирование не используется в этой статье, оно определяется здесь как точный двойник связывать.
  9. ^ В теории категорий Личность монаду также можно рассматривать как выходящую из примыкание любого функтора с обратным ему.
  10. ^ Теория категорий рассматривает эти монады коллекций как дополнения между свободный функтор и разные функторы из категория наборов к категория моноидов.

Рекомендации

  1. ^ а б c d е ж грамм час О'Салливан, Брайан; Герцен, Джон; Стюарт, Дон (2009). "Монады". Реальный мир Haskell. Севастополь, Калифорния: O'Reilly Media. Глава 14. ISBN  978-0596514983.
  2. ^ а б Вадлер, Филипп (Июнь 1990 г.). Понимание монад. Конференция ACM по LISP и функциональному программированию. Ницца, Франция. CiteSeerX  10.1.1.33.5381.
  3. ^ а б c Моджи, Эухенио (1991). «Понятия вычисления и монады» (PDF). Информация и вычисления. 93 (1): 55–92. CiteSeerX  10.1.1.158.5275. Дои:10.1016/0890-5401(91)90052-4.
  4. ^ а б c d е Вадлер, Филипп (Январь 1992 г.). Суть функционального программирования. 19-й ежегодный симпозиум ACM по принципам языков программирования. Альбукерке, Нью-Мексико. CiteSeerX  10.1.1.38.9516.
  5. ^ а б Худак, Павел; Петерсон, Джон; Фазель, Джозеф (1999). "О монадах". Мягкое введение в Haskell 98. Глава 9.
  6. ^ Ответ К. А. Макканна (23 июля 2010 г., 23:39) Как и почему работает монада Haskell Cont?
  7. ^ Спайви, Майк (1990). «Функциональная теория исключений» (PDF). Наука компьютерного программирования. 14 (1): 25–42. Дои:10.1016 / 0167-6423 (90) 90056-Дж.
  8. ^ «Законы монады». HaskellWiki. haskell.org. Получено 14 октября 2018.
  9. ^ «Чем не является Монада». 7 октября 2018.
  10. ^ Де Мойтер, Вольфганг (1997). Монады как теоретическая основа АОП (PDF). Международный семинар по аспектно-ориентированному программированию в ECOOP. Ювяскюля, Финляндия. CiteSeerX  10.1.1.25.8262.
  11. ^ "Монада (без метафор)". HaskellWiki. 1 ноября 2009 г.. Получено 24 октября 2018.
  12. ^ О'Салливан, Брайан; Герцен, Джон; Стюарт, Дон (2009). "Использование Parsec". Реальный мир Haskell. Севастополь, Калифорния: O'Reilly Media. Глава 16. ISBN  978-0596514983.
  13. ^ Стюарт, Дон (17 мая 2007 г.). "Roll Your Own Window Manager: отслеживание фокуса с помощью молнии". Control.Monad.Writer. В архиве из оригинала 20 февраля 2018 г.. Получено 19 ноября 2018.
  14. ^ Бентон, Ник (2015). «Категориальные монады и компьютерное программирование» (PDF). Лондонское математическое общество Impact150 историй. 1. Получено 19 ноября 2018.
  15. ^ Киселев, Олаг (2007). «Продолжения с разделителями в операционных системах». Моделирование и использование контекста. Springer Berlin Heidelberg. страницы 291-302. ISBN  978-3-540-74255-5.
  16. ^ Мейер, Эрик (27 марта 2012 г.). «Ваша мышь - это база данных». Очередь ACM. 10 (3). Получено 19 ноября 2018.
  17. ^ Айверсон, Кеннет (Сентябрь 1987 г.). «Словарь APL». APL Quote Quad. 18 (1): 5–40. Дои:10.1145/36983.36984. ISSN  1088-6826. Получено 19 ноября 2018.
  18. ^ Клейсли, Генрих (1965). «Любая стандартная конструкция индуцируется парой сопряженных функторов» (PDF). Труды Американского математического общества. 16 (3): 544–546. Дои:10.1090 / S0002-9939-1965-0177024-4. Получено 19 ноября 2018.
  19. ^ Питер Пеппер, изд. (Ноябрь 1997 г.). Язык программирования Opal (Технический отчет) (5-е исправленное изд.). Fachbereich Informatik, Technische Universität Berlin. CiteSeerX  10.1.1.40.2748.
  20. ^ Моджи, Эухенио (Июнь 1989 г.). Вычислительное лямбда-исчисление и монады (PDF). Четвертый ежегодный симпозиум по логике в информатике. Пасифик Гроув, Калифорния. CiteSeerX  10.1.1.26.2787.
  21. ^ а б Пейтон Джонс, Саймон Л.; Вадлер, Филипп (Январь 1993 г.). Императивное функциональное программирование (PDF). 20-й ежегодный симпозиум ACM по принципам языков программирования. Чарльстон, Южная Каролина. CiteSeerX  10.1.1.53.2504.
  22. ^ «Аппликативный функтор». HaskellWiki. Haskell.org. 7 мая 2018. В архиве с оригинала 30 октября 2018 г.. Получено 20 ноября 2018.
  23. ^ а б Гиббард, Кейл (30 декабря 2011 г.). «Монады как контейнеры». HaskellWiki. Haskell.org. В архиве из оригинала 14 декабря 2017 г.. Получено 20 ноября 2018.
  24. ^ а б Пипони, Дэн (7 августа 2006 г.). «Вы могли изобрести монады! (А может быть, вы уже это сделали)».. Окрестности бесконечности. В архиве из оригинала 24 октября 2018 г.. Получено 16 октября 2018.
  25. ^ "Некоторые подробности о вычислительных выражениях F #". Получено 9 октября 2018.
  26. ^ «Не считается вредным обозначение». HaskellWiki. Получено 12 октября 2018.
  27. ^ Джайлз, Бретт (12 августа 2013 г.). «Лифтинг». HaskellWiki. Haskell.org. В архиве из оригинала 29 января 2018 г.. Получено 25 ноября 2018.
  28. ^ а б Ривас, Экзекьель; Яскелиофф, Мауро; Шрайверс, Том (июль 2015). От моноидов до почти полуколец: суть MonadPlus и Alternative (PDF). 17-й Международный симпозиум ACM по принципам и практике декларативного программирования. Сиена, Италия. CiteSeerX  10.1.1.703.342.
  29. ^ Свиерстра, Воутер (2008). «Типы данных à la carte» (PDF). Функциональная жемчужина. Журнал функционального программирования. Издательство Кембриджского университета. 18 (4): 423–436. CiteSeerX  10.1.1.101.4131. Дои:10.1017 / s0956796808006758. ISSN  1469-7653.
  30. ^ Киселев, Олег (май 2012). Шрайверс, Том; Тиманн, Питер (ред.). Итераторы (PDF). Международный симпозиум по функциональному и логическому программированию. Конспект лекций по информатике. 7294. Кобе, Япония: Springer-Verlag. С. 166–181. Дои:10.1007/978-3-642-29822-6_15. ISBN  978-3-642-29822-6.
  31. ^ Уусталу, Тармо; Вене, Вармо (июль 2005 г.). Хорват, Золтан (ред.). Суть программирования потока данных (PDF). Первая летняя школа по функциональному программированию в Центральной Европе. Конспект лекций по информатике. 4164. Будапешт, Венгрия: Springer-Verlag. С. 135–167. CiteSeerX  10.1.1.62.2047. ISBN  978-3-540-46845-5.
  32. ^ Уусталу, Тармо; Вене, Вармо (июнь 2008 г.). «Комонадические понятия вычисления». Электронные заметки по теоретической информатике. Эльзевир. 203 (5): 263–284. Дои:10.1016 / j.entcs.2008.05.029. ISSN  1571-0661.
  33. ^ Власть, Джон; Ватанабэ, Хироши (май 2002 г.). «Объединение монады и комонады» (PDF). Теоретическая информатика. Эльзевир. 280 (1–2): 137–162. CiteSeerX  10.1.1.35.4130. Дои:10.1016 / с0304-3975 (01) 00024-х. ISSN  0304-3975.
  34. ^ Габоарди, Марко; Кацумата, Шин-я; Орчард, Доминик; Брейварт, Флавьен; Уусталу, Тармо (сентябрь 2016 г.). Комбинирование эффектов и соэффектов посредством оценивания (PDF). 21-я Международная конференция ACM по функциональному программированию. Нара, Япония: Ассоциация вычислительной техники. С. 476–489. Дои:10.1145/2951913.2951939. ISBN  978-1-4503-4219-3.

внешняя ссылка

Ссылки на HaskellWiki:

  • "Все о монадах "(первоначально Джефф Ньюберн) - всестороннее обсуждение всех распространенных монад и того, как они работают в Haskell; включает аналогию с" механизированной сборочной линией ".
  • "Типклассопедия "(первоначально Брент Йорги) - подробное описание того, как взаимосвязаны ведущие классы типов в Haskell, включая монады.

Учебники:

Интересные кейсы: