Re: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 30.10.14 13:55
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Сколько ни видел в интернете трудов наподобие "простое описание монад" — никто не может описать доходчиво. Везде одна и та же хрень: "я понял монады!" и начинается "это тип с операциями bind и return", бла-бла-бла.


Кстати, если не боишься английского, то вот хорошие статьи Бартоша Милевского для С++-ников:
http://bartoszmilewski.com/2014/02/26/c17-i-see-a-monad-in-your-future/
Тут на примере std::future разбирается, почему и как это монада и что она собой представляет (и не только монада — заодно поясняется, откуда и зачем появляются аппликативные функторы и прочая). Особенно хорошо поясняются bind и return.

И еще две статьи по ходу дела, тоже, по сути, тоже просто демонстрации того, как возникают монады и прочая:
http://bartoszmilewski.com/2014/04/21/getting-lazy-with-c/
http://bartoszmilewski.com/2014/10/17/c-ranges-are-pure-monadic-goodness/
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: Монады
От: AlexRK  
Дата: 30.10.14 13:59
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Кстати, если не боишься английского, то вот хорошие статьи Бартоша Милевского для С++-ников:

J>http://bartoszmilewski.com/2014/02/26/c17-i-see-a-monad-in-your-future/
J>Тут на примере std::future разбирается, почему и как это монада и что она собой представляет (и не только монада — заодно поясняется, откуда и зачем появляются аппликативные функторы и прочая). Особенно хорошо поясняются bind и return.

J>И еще две статьи по ходу дела, тоже, по сути, тоже просто демонстрации того, как возникают монады и прочая:

J>http://bartoszmilewski.com/2014/04/21/getting-lazy-with-c/
J>http://bartoszmilewski.com/2014/10/17/c-ranges-are-pure-monadic-goodness/

Спасибо, почитаю.
Re[5]: Монады
От: alex_public  
Дата: 02.11.14 16:57
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>А как бы это могло выглядеть на С/C++? Нужны были бы новые синтаксические конструкции?


Если интересна просто реализация полноценной монады на C++, то в предыдущей темке я кидал работающие примеры. Так что никакой спец. поддержки в языке в общем то не требуется.
Re[9]: Монады
От: alex_public  
Дата: 02.11.14 17:09
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Я так понимаю, что Haskell героически борется с проблемами, которые сам себе и создал.


Да, именно так.

ARK>А кроме задания последовательности для ввода-вывода и исключений (которые в императивные языки встроены и воспринимаются как нечто само собой разумеющееся) есть какие-то вещи, которые могли бы пригодиться и в императивщине?


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

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

T1 -> M<T2>, T2 -> M<T3>, T3 -> M<T4> и т.д.

Из известных реальных примеров в том же C++ с ходу вспоминается только future. Причём только реализация из Boost'a (в которой есть then, т.к. без этого уже не монада выходит), а не из стандарта языка.

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

P.S. Да, и вообще по поводу всего этого есть ещё один очень интересный нюанс... В очень многих случаях, когда на практике получается монада, авторы кода на самом деле даже и не думали подобным образом — они просто писали код под задачу. Возможно они даже и не в курсе, что у них там какая-то монада образовалась... )))
Re[6]: Монады
От: AlexRK  
Дата: 02.11.14 17:10
Оценка:
Здравствуйте, alex_public, Вы писали:

_>Здравствуйте, AlexRK, Вы писали:


ARK>>А как бы это могло выглядеть на С/C++? Нужны были бы новые синтаксические конструкции?


_>Если интересна просто реализация полноценной монады на C++, то в предыдущей темке я кидал работающие примеры. Так что никакой спец. поддержки в языке в общем то не требуется.


Думаю, что монаду (да и много чего еще) можно соорудить на чем угодно.
Только вот хотелось бы, чтобы это выглядело не очень страшно.
Re[5]: Монады
От: alex_public  
Дата: 02.11.14 17:11
Оценка:
Здравствуйте, x-code, Вы писали:

XC>Но, был бы еще понятнее четвертый вариант:

XC>
Maybe<int> Add3(Maybe<int> a, Maybe<int> b)
XC>{
XC>   return a+b;
XC>}

XC>и чтобы оно само все разрулилось как надо. На этапе компиляции, выполнения — не важно. Интересно, такое возможно?

Да, это лифтинг операторов. В императивных языках без проблем реализуется через перегрузку.
Re[6]: Монады
От: alex_public  
Дата: 02.11.14 17:15
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>В C# можно разрулить перегрузками операторов, но это далеко не все случаи покрывает.

G>Например такой код:
G>
G>from v1 in a
G>from v2 in f(v1)
G>select g(v1,v2)
G>

G>Уже никакими перегрузками не покроешь.

В D без проблем это реализуется. Я кидал работающий пример в предыдущей темке.

G>НО в haskell есть функция lift, которая позволяет "поднять" оператор в монаду.

G>Это страшное словосочетание говорит, что имея оператор T `x` V => U и монаду М (тип и две функции) автоматически преобразуется в M<T> `mx` M<V> => M<U>.
G>Теоретически в Haskell можно автоматом "поднимать" все операторы для всех монад в текущем контексте, практически мало пользы, ибо самое интересное не описывается арифметическими операторами.

Ну так Хаскель и совсем не идеал. Языки с нормальным метапрограммированием очевидно будут сильнее в таких вещах.
Re[7]: Монады
От: alex_public  
Дата: 02.11.14 17:27
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Думаю, что монаду (да и много чего еще) можно соорудить на чем угодно.

ARK>Только вот хотелось бы, чтобы это выглядело не очень страшно.

Ну на плюсах использование выглядит ничуть не страшнее Хаскеля) Только вот смысл использовать здесь монады появляется намного реже. )
Re[3]: Монады
От: maxkar  
Дата: 03.11.14 21:35
Оценка:
Здравствуйте, x-code, Вы писали:

Вот здесь
Автор: D. Mon
Дата: 30.10.14
D. Mon очень хорошо все расписал.

XC>Сама по себе концепция этой обертки уже очень интересна, без всяких монад. Есть ли какое-то общее определение этой "обертки" Что еще может быть оберткой?

Я точных терминов не знаю. Да, оно связано с теорией категорий. Еще какую-то информацию можно найти по "higher kinded types" и "type constructor". Вот Box — это конструктор типа и при применении к типу T дает в результате Box<T>.

XC>То есть конструктор будущей монады? Берем простое значение и заворачиваем его в бокс. А вот кстати, чтобы взять обратно значение из бокса есть что-нибудь?

Из бокса в общем случае ничего взять нельзя. Он односторонний. В конкретных реализацих (вроде either/option/list) это сделать можно. Но от монад этого не требуется, и не всегда это можно сделать (IO, Async).
Box.return скорее конструктор будущего значения. Монада — это вроде бы сам Box (как конструктор типа). Но для понимания это не важно.

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

Да, именно так.

XC>То есть метод map() или apply() сделали статической функцией, а бокс ей передаем в качестве второго аргумента? ОК. Пока непонятно зачем это.

Мне это нужно с сугубо практической точки зрения. Чтобы варианты с одним аргументом и несколькими друг от друга не очень отличались. Плюс функция вернулась в привычное место — слева от аргумента. То, что D. Mon написал, тоже верно. Плюс в haskell оно достаточно активно используется (там оно в форме lift: (A -> B) -> Box<A> -> Box<B>, именно с таким каррированием) и именно в применениях к функции. В других языках это тоже допустимо, но на практике вроде бы применяется не часто.

XC>На мой неопытный взгляд тут расхождение со статьей на Хабре, на оригинал которой вы ссылались.

XC>Там написано что "аппликативный функтор" это когда функция тоже упакована в контейнер, а у вас нечто другое — когда функция применяется для группы "боксов" сразу. Или я чего-то не понял?
Все правильно. Именно с того места мое изложение отходит от стандартной формулировки понятий. Я записываю applicative в "неканонической" форме, при этом эквивалентной исходной. D. Mon одну половинку (выражение функции от многих аргументов через аппликативное применение) показал. Обратное — аналогично. Я бы его как
fn : Box<A -> B>
v : Box<B>

fn <*> v == Box.apply($, fn, v) //или Box.apply(applyFn, [fn, v])

// $ - это в haskell применение функции, так же, как и строка ниже
$ fn x = applyFn(fn, x) = fn(x)


На самом деле ситуация даже интереснее. Каноничная форма записи applicative вызывана системой типов языков, где оно появилось. В haskell ведь все функции от одного аргумента и могут возвращать другие функции. Т.е. "функция от двух аргументов A и B" превращается в "функцию от аргумента A, возвращающую функцию от аргумента B". На практике проблемой не является (а во многих случаях является преимуществом), но накладывает свой отпечаток на разные API.
Подумайте, откуда у вас будет функция внутри Box. Можно сделать return function. Но какой смысл, если у нас уже есть функтор и можно применить функцию с значению? Т.е. вместо (return fn) <*> value можно написать сразу fn <$> value (fmap fn value или даже fn `fmap` value, это все одно и то же). Во многих случаях функции попадают внутрь Box в результате применения к ним функтора или другого аппликативного применения. Т.е. была "чистая" функция возвращающая функцию (A -> B -> C). Ее применили к значению внутри бокса Box<A> и только после этого получили функцию внутри бокса Box<B -> C>. И вот потом уже этот результат (бокс с функцией) аппликативно применяется к боксу со следующим значением.
В очень многих случаях вы увидите
fn1 : A -> B -> C
fn2 : A -> B -> C -> D
v1 : Box<A>
v2 : Box<B>
v3 : Box<C>
r1 : Box<C> = fn1 <$> v1 <*> v2 == Box.apply(Box.map(fn1, v1), v2)
r2 : Box<D> = fn2 <$> v1 <*> v2 <*> v3 == 
  Box.apply(Box.apply(Box.map(fn1, v), v2), v3)

Если язык поддерживает манипуляции с функциями от многих аргументов (javascript, lisp) это можно свернуть в один вызов "обобщенной" функции. Формализмы получаются разные, но в результате эквивалентные друг другу. А вот основная идея и практические применения у них общие.
Re[6]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 05.11.14 14:00
Оценка:
Здравствуйте, alex_public, Вы писали:

_>Да, это лифтинг операторов. В императивных языках без проблем реализуется через перегрузку.


Ты вроде бы сам показал пример на тему 'почему нельзя назвать "без проблем"'
Re[2]: Монады
От: uncommon Ниоткуда  
Дата: 06.11.14 05:49
Оценка: -1
Здравствуйте, jazzer, Вы писали:

J>Кстати, если не боишься английского, то вот хорошие статьи Бартоша Милевского для С++-ников:


Не люблю я этого Бартоша. По-моему, он просто несёт около-программистский бред. Такое ощущение, что в C++ он не очень силён, что он пытается скомпенсировать примерчиками из Хаскеля (достаточно примитивными, должен сказать). В результате получается полная муть.
Re[3]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 06.11.14 07:12
Оценка:
Здравствуйте, uncommon, Вы писали:

U>Здравствуйте, jazzer, Вы писали:


J>>Кстати, если не боишься английского, то вот хорошие статьи Бартоша Милевского для С++-ников:


U>Не люблю я этого Бартоша. По-моему, он просто несёт около-программистский бред. Такое ощущение, что в C++ он не очень силён, что он пытается скомпенсировать примерчиками из Хаскеля (достаточно примитивными, должен сказать). В результате получается полная муть.


Это ты прочитал статьи, ссылки на которые я дал выше, я правильно понимаю?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[7]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 06.11.14 10:50
Оценка:
Здравствуйте, alex_public, Вы писали:

G>>Теоретически в Haskell можно автоматом "поднимать" все операторы для всех монад в текущем контексте, практически мало пользы, ибо самое интересное не описывается арифметическими операторами.


_>Ну так Хаскель и совсем не идеал. Языки с нормальным метапрограммированием очевидно будут сильнее в таких вещах.


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

return  f(g(h(x)))


Вот такая строчка уже ни о чем не говорит. ты просто не знаешь, чего ждать от выражения, потому что самое главное будет "где-то рядом", "где-то здесь", "где-то там"
Re: Монады
От: Кодт Россия  
Дата: 11.11.14 15:16
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Может кто-нибудь описать простым языком — что такое монады и зачем они нужны?


Если коротко, то монада — это любая обёртка данных, подобная списку. Возможно — списку из одного элемента (IO), возможно, из нуля-или-одного (Maybe).

Если ты знаком с нотацией set comprehension, то всё окажется довольно просто.

1. Списочная функция берёт аргументы и возвращает список результатов. f(x) = [...]
2. Если каждый аргумент взят из своего списка, то мы получаем список списков: [ f(x) for x in Xs ] = [ [...], [...], ..... ]; этот список автоматически расплющивается через конкатенацию
Fs = [...]++[...]++[...]
3. Композиция источников f(g(x)): flatten([ g(y) for x in Xs for y in f(x) ])

У всякой монады есть тривиальный конструктор, обёртка одного значения: [x]
Зачастую, у монады есть нулевой элемент, подобный пустому множеству: []. Кстати, у IO его нет.
Если функция f(x) вернула пустой список, то забег по for y выродится, и нечего будет подавать на вход g(y), и соответственно, нечего будет расплющивать.

Нулевой элемент используется, таким образом, для мгновенного завершения вычислений.
Это Nothing у Maybe, это Left smth у Either, ну и собственно пустой список.

Монада, помимо списка результатов, может содержать что-то полезное сбоку.
Тогда процедура вызова чуть усложняется:
def f(token, x):
  .....
  return token2, [a,b,c]

def g(token, y):
  .....
  return token3, [d,e,f]


def call_augmented(g, txs):
  t,xs = txs
  zs = []
  for x in xs:
    t,ys = g(t,x)
    zs += ys # сразу же и сплющиваем
  return t,zs

txs = token0,[x1,x2,x3]
tys = call_augmented(f, txs)
tzs = call_augmented(g, tys)


Функция может быть прозрачной для элементов, и заниматься подправлением этой побочной информации
def modify_token(token, x):
  return token+1, [x]

def extract_token(token, x):
  return token, [token]

def check_token(token, x):
  if token is bad :
     return token, []
  else:
     return token, [x]



На питоне, кстати, с его генераторами, сопрограммами и конструкторами списков писать монадический код — полтора удовольствия.
Там и сахар присобачить можно, и карринг. Только do-нотация немножко ногами пишется.


ARK>Или монады это такая сложная концепция, что описать ее доступным языком невозможно в принципе?


Да авторы хаскелла нашли удачный базис и воспользовались. А вот другие сущности из теорката — стрелки, — чего-то не выстрелили. Сейчас вроде модно использовать аппликативные функторы.
Перекуём баги на фичи!
Re[13]: Монады
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.11.14 19:44
Оценка:
Здравствуйте, AlexRK, Вы писали:
ARK>Можете продемонстрировать, что будет достигнуто путем такой замены?
CFRONT.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Монады
От: omgOnoz  
Дата: 18.11.14 09:49
Оценка:
Здравствуйте, maxkar, Вы писали:

  Скрытый текст
M>Здравствуйте, AlexRK, Вы писали:

ARK>>Может кто-нибудь описать простым языком — что такое монады и зачем они нужны?


M>Я могу! И отвечу еще на ряд вопросов, заданных в ветке.


M>Монады в классическом виде такие страшные, потому что помимо основной идеи еще идет борьба с системой типов языка. Концепция на самом деле более универсальная и удобная.


M>Теперь по порядку. Введение в весь стек с монадами в картинках. Полезно, если любите графическое представление. Плюс до функторов (включительно) я буду говорить примерно о том же.


M>

Значения, функции и Box

M>С примитивными (и не только) значениями все вроде бы понятно. Это обычные значения. Функции — тоже вполне классические функции, включая функции от многих аргументов:
M>
M>f1 : T => R
M>f2 : (T1, T2, T3, ..., TN) => R
M>


M>Для введения дальнейших понятий нам нужен еще какой-нибудь контейнер, в который можно "положить" значение любого типа. Пусть это будет Box<T>. Как у него семантика — зависит от реализации. Это может быть Option, может быть Either, может быть асинхронное выполнение, observable, collection, etc... Важно только то, что это некая "обертка" поверх типа.


M>Здесь же можно отметить, что у Box должен быть метод "упаковки" простого значения в контейнер. Например, так:

M>
M>Box.box : T => Box<T>
M>//применение
M>val boxed : Box<Int> = Box.box(3)
M>

M>Это является аналогом монадного return.

M>

Functor

M>Просто складывать значения в Box не интересно (можно складывать Box в Box, но это тоже быстро надоест). Хочется сделать что-нибудь с Box<T>. Например, у нас Box — асинхронная задача. И хочется что-нибудь запустить после завершения результата. Другими словами, применить функцию к результату операции. Очевидно, что применение "чистой функции" к асинхронной операции может применить эту функцию не сразу, а по завершению операции. Т.е "природа" Box сохраняется.

M>Можно сделать применение такой функции, например, так:

M>
M>class Box<T> {
M>  // какая-то реализация семантики Box
  
M>  //Применение функции "внутри" box 
M>  def map<R>(fn : T => R) : Box<R>
M>}

M>// Применение к асинхронной операции
M>val operation : Async<Image> = loadImage(...);
M>operation.map(image => alert("Она загрузилась!"));
M>

M>Это может быть применение функции к результату асинхронной операции, применение функции к элементу коллекции/option/either и т.д. в зависимости от семантики Box. Вот эта вот функция map называется функтором.

M>Если вы внимательно посмотрите, это очень напоминает Promise. И это очень неудачный вариант предоставления функтора! Например, почти эквивалентно будет

M>
M>class Box<T> {
M>  static def<T, R> map(fn : T => R, box : Box<T>) : Box<R>
M>}

M>//Применение к асинхронной операции:
M>val operation : Async<Image> = loadImage(...);
M>Async.map(image => alert("Оно загрузилось"), operation);

M>//Или более реалистичный пример, с дополнительными функциями
M>val operation : Async<Image> = loadImage(...);
M>Async.apply(showImage, operation);

M>def showImage(image : Image) : void {
M>  //....
M>}
M>

M>Казалось бы, не такая и большая разница между двумя API. На самом деле очень большая, потому что мы будем обобщать применение функций дальше. Итак, следующая секция:

M>

Applicatives

M>Усложним задачу. Теперь нужно грузить не один ресурс, а несколько. Например, картинку с нашего сервера и какие-то данные с сервера партнеров. И после этого отображать окно интерфейса. Т.е. у нас уже есть
M>
M>val imageOp : Async<Image> = loadImage(...);
M>val partnerData : Async<SpamData> = loadPartnerData(...);

M>def showUI(image : Image, data : SpamData) : void {
M>  //...
M>}
M>

M>И теперь нам нужно бы связать операции с целевой функцией. Но ведь в предыдущей секции мы что-то подобное уже делали! Можно попробовать сделать точно так же:
M>
M>Box.map(showUI, imageOp, partnerData);
M>

M>Т.е. нам нужно обобщить применение функции с одного Box на несколько. Если система типов не сильно мешает, можно сделать и прямолинейно:
M>
M>class Box<T> {
M>  static def map<T1, T2, T3, ..., TN, R>(
M>    fn : (T1, T2, T3, ..., TN) => R,
M>    a1 : Box<T1>, a2 : Box<T2>, a3 : Box<T3>, ..., an : Box<TN>) : Box<R>
M>}

M>// Вполне реальный для JS API:
M>// Такими могли бы быть Promise! 
M>// И Promise достаточно легко допиливаются до этого состояния.
M>var imageOp = loadImage(...);
M>var spamData = loadPartnerData(...);
M>Aysnc.apply(showUI, [imageOp, spamData]);

M>// Сравните с обычным (синхронным) применением:
M>var image = getSomeImage();
M>var spamData = getSomeData();
M>showUI(image, spamData);
M>

M>Вот так получается, если система типов не мешает. Мы обобщили "функтор" на несколько аргументов и получили нечто. Вот именно для получения этого нечто и сделан Applicative в haskell и аналогичных языках! В первую очередь для обобщения применения функции к нескольким Box'ам в рамках системы типов. И на практике именно так аппликативы и применяются:
M>
M>fn <$> arg1 <*> arg2 <*> arg3
M>


M>В то же время это очень похоже на обычное применение функции к аргументам. Только теперь функция применяется не к самим аргументам, а к Box<arg>.


M>

Монады

M>Пойдем еще дальше. Нам нужно грузить некоторые данные в зависимости от уже полученных данных. Например, получив данные от партнера мы хотим еще загрузить картинку. Т.е. у нас есть функция, которая по входным данным запускает некоторый процесс и мы хотим применить ее к асинхронно загружаемым данным.
M>
M>var partnerData : Async<Int> = loadPartnerData(...);
M>var otherPartnerData : Async<String> = loadPartnerData(...);
M>var evenMoreData : Async<String> = loadEvenMoreData(...);

M>def loadDependentData(d1 : Int, d2 : String, d3 : String) : Async<Image> {
M>  final String imageUrl = d2 + d1 + d3;
M>  return loadImage(imageUrl);
M>}
M>

M>Использовать Async.apply не пройдет по типам, у нас будет Async<Async<Image>>. Но мы пока изобратаем API. Так что давайте добавим еще один метод:

M>
M>class Box<T> {
M>  static def mmap<T1, T2, T3, ..., TN, R>(
M>    fn : (T1, T2, T3, ..., TN) => Box<R>,
M>    a1 : Box<T1>, a2 : Box<T2>, a3 : Box<T3>, ..., an : Box<TN>) : Box<R>
M>}

M>// Применение!
M>// И это тоже стоило бы сделать в Promise, но...
M>var partnerImage : Box<Image> = Async.mapply(loadDependentData, partnerData, otherPartnerData, evenMoreData);
M>Async.apply(showUI, partnerImage);
M>

M>Вот этот вот mmap + Box.box из первого пункта и дают монаду . Да, с формальной точки зрения это не монада (у монады другой формализм). А вот решаемая задача — именно эта. И на самом деле классичесаая монада эквивалентна вот этой "обобщенной функции" mmap. Они достаточно просто выражаются друг через друга. Зато с практической точки зрения мой вариант понятнее. И заодно показывает, как примерно монады применяются. На самом деле в таком "аппликативном стиле" они удобны даже в императивных языках (в примерах показан Async, именно с таким API я себе делал его и успешно использовал).

M>

Всё вместе

M>Сводная табличка идей (cheat sheet):
M>
M>// function application
M>var a : T1 = ..., b : T2 = ..., c : T3 = ...;
M>def f(x1 : T1, x2 : T2, x3 : T3) : R
M>var r : R = f(a, b, c);

M>// Functor
M>// Только для одноаргументных функций
M>var a : Box<T1> = ...;
M>def f(x1 : T1) : R
M>var r : Box<R> = Box.apply(f, a);

M>// "Applicative"
M>var a : Box<T1> = ..., b : Box<T2> = ..., c : Box<T3> = ...;
M>def f(x1 : T1, x2 : T2, x3 : T3) : R
M>var r : Box<R> = Box.apply(f, a, b, c);

M>// "Monad"
M>var a : Box<T1> = ..., b : Box<T2> = ..., c : Box<T3> = ...;
M>def f(x1 : T1, x2 : T2, x3 : T3) : Box<R>
M>var r : Box<R> = Box.mapply(f, a, b, c);
M>


M>

Немного практики

M>Теперь о практической части. Да, я использовал и использую монады в императивщине. Например, в виде API из cheat sheet оно использовалось в AS3 (язык позволяет такие фокусы) для реактивного программирования и асинхронных операций. Вполне успешно и удобно. Сейчас есть "посмореть" библиотечка аппликативного реактивного программирования для scala. Вот там в силу языка аппликативы и монады гораздо больше похожи на классичесчкий вариант. Есть даже небольшой пример использования библиотечки в действии (не дописан, но смотреть можно, там уже база есть). Искать использование по ":<" и ":>" (без кавычек).

M>Плюсы этих "монад" для практики. Большую часть приложения все еще можно изобразить в чистых функциях. Их удобно писать и тестировать. Какая-то небольшая часть приложения работает с "грязными" данными (в виде асинхронных операций, текущего изменяемого состояния представленного в виде Behavior, etc...). Применение функций в этих классах грязных данных тоже оттестировано (functor, applicative, monad это 4 операции на каждый класс). В результате чего места для ошибок практически нет (не надо вручную все компоненты синхронизировать, например). Ну и вид программы более декларативный. Я объявляю те же компоненты интерфейса (и текущий вид) как функцию от входных данных и состояний, а не как набор методов по копированию всего подряд из модели в вид и обратно.


Меня больше всего смущает используемый синтаксис. Хотелось, бы видеть, что-то членораздельное. Пока что — вырви глаз.

Очередной раз пытаюсь понять, но видя такой синтаксис понимаю, что я никогда их в таком виде не буду использовать, просто из-за синтаксиса.
Отредактировано 18.11.2014 9:53 omgOnoz . Предыдущая версия . Еще …
Отредактировано 18.11.2014 9:53 omgOnoz . Предыдущая версия .
Отредактировано 18.11.2014 9:51 omgOnoz . Предыдущая версия .
Re: Монады
От: JazzzMaster Россия  
Дата: 21.11.14 07:05
Оценка: :)
Здравствуйте, AlexRK, Вы писали:

ARK>Товарищи!


ARK>Может кто-нибудь описать простым языком — что такое монады и зачем они нужны?


Ну это мужские гонады, сокращенно монады.
Зачем нужны, думаю, сам найдешь в интернете.
Re: Монады
От: barn_czn  
Дата: 29.12.14 08:08
Оценка: :)
сын мой, монада есть высшее знание . сие знание приходит из космоса, в трудах и тренировках..
не все чакры значит открыл ты для этого
Re: Монады
От: andyag  
Дата: 12.01.15 13:34
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Или монады это такая сложная концепция, что описать ее доступным языком невозможно в принципе?


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