Монада на простом английском? (Для программиста ООП без фона FP)

Yo может получить URL-адрес favicon с HTML-сайта веб-сайта.

Вот тег favicon:

<link rel="icon" type="image/png" href="/someimage.png" />

Здесь вы должны использовать регулярное выражение. Если тег не найден, найдите «favicon.ico» в корневом каталоге сайта. Если ничего не найдено, на сайте нет значка.

672
задан 24 April 2010 в 20:13
поделиться

8 ответов

Самое простое объяснение, о котором я могу думать, состоит в том, что монады являются способом составить функции с украшенными результатами (иначе состав Kleisli). Функция "embelished" имеет подпись a -> (b, smth), где a и b типы (думайте Int, Bool), который мог бы отличаться друг от друга, но не обязательно - и smth "контекст" или "embelishment".

Этот тип функций может также быть записан a -> m b, где m эквивалентно "embelishment" smth. Таким образом, это функции, которые возвращают значения в контексте (думайте функции, которые регистрируют их действия, где smth регистрирующееся сообщение; или функции, которые выполняют input\output и их результаты, зависят от результата действия IO).

монада А является интерфейсом ("typeclass"), который заставляет реализатора сказать ее, как составить такие функции. Реализатор должен определить функцию состава (a -> m b) -> (b -> m c) -> (a -> m c) для любого типа m, который хочет реализовать интерфейс (это - состав Kleisli).

Так, если мы говорим, что у нас есть тип "кортеж" (Int, String) результаты представления вычислений на [1 113] с, которые также регистрируют их действия, с [1 114] являющийся "embelishment" - журналом действия - и двумя функциями increment :: Int -> (Int, String) и twoTimes :: Int -> (Int, String), мы хотим получить функцию incrementThenDouble :: Int -> (Int, String), который является составом двух функций, который также принимает во внимание журналы.

На данном примере, реализация монады двух функций относится к целочисленному значению 2 incrementThenDouble 2 (который равен [1 119]), возвратился бы (6, " Adding 1. Doubling 3.") для посреднических результатов increment 2 равный [1 122] и twoTimes 3 равный [1 124]

От этой функции состава Kleisli, можно получить обычные одноместные функции.

0
ответ дан 22 November 2019 в 21:35
поделиться

Из википедии :

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

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

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

Я считаю, что это очень хорошо объясняет.

11
ответ дан 22 November 2019 в 21:35
поделиться

Если вы когда-либо использовали Powershell, шаблоны, описанные Эриком, должны звучать знакомо. Командлеты Powershell являются монадами; функциональный состав представлен конвейером.

Интервью Джеффри Сновера с Эриком Мейером более подробное.

1
ответ дан 22 November 2019 в 21:35
поделиться

ОБНОВЛЕНИЕ. Этот вопрос был предметом очень длинной серии блогов, которую вы можете прочитать по адресу Monads - спасибо за отличный вопрос!

В терминах, понятных программисту ООП (без какого-либо опыта функционального программирования), что такое монада?

Монада - это «усилитель» типов , подчиняющихся определенным правилам и , в котором предусмотрены определенные операции .

Во-первых, что такое «типовой усилитель»? Под этим я подразумеваю некоторую систему, которая позволяет вам взять тип и превратить его в более особенный тип. Например, в C # рассмотрим Nullable . Это типовой усилитель. Он позволяет вам взять тип, например int , и добавить к этому типу новую возможность, а именно, что теперь он может иметь значение NULL, хотя раньше не мог.

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

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

int M(int x) { return x + N(x * 2); }

, тогда соответствующая функция на Nullable может заставить все операторы и вызовы работать вместе «таким же образом», как и они. до.

(Это невероятно расплывчато и неточно; вы попросили объяснение, которое не предполагает ничего о знании функционального состава.)

Что такое «операции»?

  1. Существует «единичная» операция (иногда ошибочно называемая операцией «возврата»), которая берет значение из простого типа и создает эквивалентное монадическое значение. По сути, это дает возможность взять значение неусиленного типа и превратить его в значение усиленного типа. Он может быть реализован как конструктор на языке объектно-ориентированного программирования.

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

  3. Часто есть способ вернуть неусиленный тип из усиленного. Строго говоря, для монады эта операция не требуется. (Хотя это необходимо, если вы хотите иметь комонаду . Мы не будем рассматривать их далее в этой статье.)

Снова возьмем Nullable в качестве примера . Вы можете превратить int в Nullable с помощью конструктора.Компилятор C # позаботится о самом «подъеме», допускающем значение NULL, но если этого не произошло, преобразование подъема будет простым: операция, скажем,

int M(int x) { whatever }

преобразуется в

Nullable<int> M(Nullable<int> x) 
{ 
    if (x == null) 
        return null; 
    else 
        return new Nullable<int>(whatever);
}

и превращение Nullable обратно в int выполняется с помощью свойства Value .

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

Предположим, у вас есть функция от int до int , как наш оригинальный M . Вы можете легко превратить это в функцию, которая принимает int и возвращает Nullable , потому что вы можете просто запустить результат через конструктор, допускающий значение NULL. Теперь предположим, что у вас есть этот метод высшего порядка:

static Nullable<T> Bind<T>(Nullable<T> amplified, Func<T, Nullable<T>> func)
{
    if (amplified == null) 
        return null;
    else
        return func(amplified.Value);
}

Посмотрите, что вы можете с ним сделать? Любой метод, который принимает int и возвращает int или принимает int и возвращает Nullable может теперь к нему применена семантика, допускающая обнуление .

Кроме того: предположим, что у вас есть два метода

Nullable<int> X(int q) { ... }
Nullable<int> Y(int r) { ... }

, и вы хотите их составить:

Nullable<int> Z(int s) { return X(Y(s)); }

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

Nullable<int> Z(int s) { return Bind(Y(s), X); }

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

В C # Bind называется SelectMany. Посмотрите, как это работает с монадой последовательности. Нам нужны две вещи: превратить значение в последовательность и связать операции с последовательностями. В качестве бонуса у нас также есть «превратить последовательность обратно в значение».Вот эти операции:

static IEnumerable<T> MakeSequence<T>(T item)
{
    yield return item;
}
// Extract a value
static T First<T>(IEnumerable<T> sequence)
{
    // let's just take the first one
    foreach(T item in sequence) return item; 
    throw new Exception("No first item");
}
// "Bind" is called "SelectMany"
static IEnumerable<T> SelectMany<T>(IEnumerable<T> seq, Func<T, IEnumerable<T>> func)
{
    foreach(T item in seq)
        foreach(T result in func(item))
            yield return result;            
}

Правило монады, допускающей значение NULL, заключалось в том, чтобы «объединить две функции, которые производят значения NULL, вместе, проверить, приводит ли внутренняя функция к NULL; если да, то произвести NULL, если нет, то вызвать внешнюю. с результатом ». Это желаемая семантика nullable.

Правило монады последовательностей состоит в том, чтобы «объединить две функции, которые производят последовательности вместе, применить внешнюю функцию к каждому элементу, произведенному внутренней функцией, а затем объединить все результирующие последовательности вместе». Фундаментальная семантика монад зафиксирована в методах Bind / SelectMany ; это метод, который сообщает вам, что на самом деле означает монада .

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

static IEnumerable<U> SelectMany<T,U>(IEnumerable<T> seq, Func<T, IEnumerable<U>> func)
{
    foreach(T item in seq)
        foreach(U result in func(item))
            yield return result;            
}

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

Какую проблему он решает и в каких наиболее распространенных местах он используется?

Это похоже на вопрос: «Какие проблемы решает одноэлементный шаблон?», Но я попробую.

Монады обычно используются для решения таких проблем, как:

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

C # использует монады в своем дизайне. Как уже упоминалось, шаблон, допускающий значение NULL, очень похож на «возможно монаду». LINQ полностью построен из монад; метод SelectMany выполняет семантическую работу по композиции операций. (Эрик Мейер любит указывать на то, что каждая функция LINQ может быть реализована с помощью SelectMany ; все остальное - просто удобство.)

Чтобы прояснить то понимание, которое я искал, допустим, вы преобразовывали приложение FP, которое имело монады, в приложение ООП. Что бы вы сделали, чтобы перенести обязанности монад в приложение ООП?

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

Хорошее место для начала - то, как мы реализовали LINQ в C #.Изучите метод SelectMany ; это ключ к пониманию того, как монада последовательности работает в C #. Это очень простой метод, но очень мощный!


Предлагается для дальнейшего чтения:

  1. Для более глубокого и теоретически обоснованного объяснения монад в C # я настоятельно рекомендую статью моего коллеги ( Эрика Липперта ) по этому поводу Уэса Дайера. Эта статья - то, что объяснило мне монады, когда они наконец "щелкнули" для меня.
  2. Хорошая иллюстрация того, почему вам может понадобиться монада (в примерах используется Haskell) .
  3. Что-то вроде «перевода» предыдущей статьи на JavaScript.

701
ответ дан 22 November 2019 в 21:35
поделиться

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

  • return x создает значение типа монады, которое инкапсулирует x
  • m >> = f (читается как «оператор связывания») применяет функцию f к значению в монаде m

Вот что такое монада. Есть еще несколько технических деталей , но в основном эти две операции определяют монаду. Настоящий вопрос: «Что делает монада ?», И это зависит от монады: списки - это монады, Maybes - это монады, операции ввода-вывода - это монады. Когда мы говорим, что эти объекты являются монадами, это означает только то, что они имеют интерфейс монад return и >> = .

25
ответ дан 22 November 2019 в 21:35
поделиться

Я бы сказал, что ближайшей OO-аналогией монадам является "командный паттерн".

В командном шаблоне вы оборачиваете обычный оператор или выражение в объект command. Объект command раскрывает метод execute, который выполняет обернутый оператор. Таким образом, оператор превращается в объект первого класса, который можно передавать и выполнять по своему усмотрению. Команды могут быть составлены, так что вы можете создать программу-объект путем цепочки и вложенности команд-объектов.

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

Шаблон команд может быть использован для добавления (или удаления) возможностей языка, которые не поддерживаются основным языком. Например, в гипотетическом ОО-языке без исключений можно добавить семантику исключений, открыв командам методы "try" и "throw". Когда команда вызывает throw, вызывающий возвращается назад по списку (или дереву) команд до последнего вызова "try". И наоборот, вы можете удалить семантику исключений из языка (если вы считаете, что исключения - это плохо), перехватывая все исключения, вызванные каждой отдельной командой, и превращая их в коды ошибок, которые затем передаются следующей команде.

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

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

74
ответ дан 22 November 2019 в 21:35
поделиться

У вас есть недавняя презентация « Monadologie - профессиональная помощь при типовой тревоге » от Christopher League (12 июля 2010 г.), которая вполне интересно по темам продолжения и монады.
Видео, сопровождающее эту (показ слайдов) презентации, на самом деле доступно на vimeo .
Часть Monad начинается примерно через 37 минут в этом часовом видео и начинается со слайда 42 из 58 слайд-презентации.

Он представлен как «ведущий шаблон проектирования для функционального программирования», но в примерах используется язык Scala, который одновременно является ООП и функциональным.
Вы можете узнать больше о монаде в Scala в сообщении блога « Монады - Другой способ абстрактных вычислений в Scala » из Дебасиша Гоша (27 марта 2008 г.).

Конструктор типа M является монадой, если он поддерживает следующие операции:

# the return function
def unit[A] (x: A): M[A]

# called "bind" in Haskell 
def flatMap[A,B] (m: M[A]) (f: A => M[B]): M[B]

# Other two can be written in term of the first two:

def map[A,B] (m: M[A]) (f: A => B): M[B] =
  flatMap(m){ x => unit(f(x)) }

def andThen[A,B] (ma: M[A]) (mb: M[B]): M[B] =
  flatMap(ma){ x => mb }

Так, например (в Scala):

  • Option - это монада
    def unit[A] (x: A): Option[A] = Some(x)

    def flatMap[A,B](m:Option[A])(f:A =>Option[B]): Option[B] =
      m match {
       case None => None
       case Some(x) => f(x)
      }
  • List is Monad
    def unit[A] (x: A): List[A] = List(x)

    def flatMap[A,B](m:List[A])(f:A =>List[B]): List[B] =
      m match {
        case Nil => Nil
        case x::xs => f(x) ::: flatMap(xs)(f)
      }

Монада имеет большое значение в Scala из-за удобного синтаксиса, созданного для использования преимуществ структур Monad:

для понимания в Scala :

for {
  i <- 1 to 4
  j <- 1 to i
  k <- 1 to j
} yield i*j*k

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

(1 to 4).flatMap { i =>
  (1 to i).flatMap { j =>
    (1 to j).map { k =>
      i*j*k }}}

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

В приведенном выше фрагменте flatMap принимает в качестве входных данных замыкание (SomeType) => List [AnotherType] и возвращает List [AnotherType] . Важно отметить, что все плоские карты принимают один и тот же тип замыкания в качестве входных и возвращают тот же тип в качестве выходных.

Это то, что «связывает» вычислительный поток - каждый элемент последовательности в for-computing должен соблюдать это ограничение типа.


Если вы выполните две операции (которые могут потерпеть неудачу) и передадите результат третьей, например:

lookupVenue: String => Option[Venue]
getLoggedInUser: SessionID => Option[User]
reserveTable: (Venue, User) => Option[ConfNo]

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

val user = getLoggedInUser(session)
val confirm =
  if(!user.isDefined) None
  else lookupVenue(name) match {
    case None => None
    case Some(venue) =>
      val confno = reserveTable(venue, user.get)
      if(confno.isDefined)
        mailTo(confno.get, user.get)
      confno
  }

, тогда как с Monad вы можете работайте с фактическими типами ( Venue , User ), как и все операции, и держите элементы проверки Option скрытыми, все из-за плоских карт синтаксиса for:

val confirm = for {
  venue <- lookupVenue(name)
  user <- getLoggedInUser(session)
  confno <- reserveTable(venue, user)
} yield {
  mailTo(confno, user)
  confno
}

Часть yield будет выполнена, только если все три функции имеют Some [X] ; любой Нет не будет напрямую возвращен на подтверждение .


Итак:

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

И самая большая сила заключается в способности составлять монады, которые служат разным целям, в расширяемые абстракции внутри приложения.

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


Между прочим, Монада - это не только модель вычислений, используемая в FP:

Теория категорий предлагает множество моделей вычислений. Среди них

  • Модель вычислений Стрелка
  • Модель вычислений Монада
  • Аппликативная модель вычислений
42
ответ дан 22 November 2019 в 21:35
поделиться

В терминах, которые программист ООП мог бы понять (без функционала фон программирования), что такое монада?

Какую проблему он решает и что наиболее часто ли она используется? наиболее часто ли она используется?

С точки зрения объектно-ориентированного программирования, монада - это интерфейс (или, что более вероятно, миксин), параметризованный типом с двумя методами, return и bind , которые описывают:

  • Как ввести значение, чтобы получить монадическое значение введенного значения type;
  • Как использовать функцию, делает монадическое значение из немонадический, по монадическому значению.

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

Более конкретно, Monad « interface »похож на IEnumerator или IIterator в том, что он принимает тип, который сам принимает тип. Однако основной «точкой» Монады является возможность соединять операции на основе внутреннего типа, вплоть до появления нового «внутреннего типа», сохраняя - или даже улучшая - информационную структуру основной класс.

61
ответ дан 22 November 2019 в 21:35
поделиться