Монады
От: AlexRK  
Дата: 26.10.14 19:20
Оценка: :))) :)
Товарищи!

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

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

Или монады это такая сложная концепция, что описать ее доступным языком невозможно в принципе?
Re: Монады
От: dimgel Россия https://github.com/dimgel
Дата: 26.10.14 19:22
Оценка:
Здравствуйте, AlexRK, Вы писали:

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


Вот же, тремя ветками ниже уже с полгода обсуждают: http://rsdn.ru/forum/philosophy/5380969
Автор: dimgel
Дата: 03.12.13
Re: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 26.10.14 19:27
Оценка: +1
Здравствуйте, AlexRK, Вы писали:

ARK>Товарищи!


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


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


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


Это способ связать вычисления каким-либо образом. Паттерн, если угодно. Выраженный прямо в языке.
Например, строго последовательное вычисление (сюда же и ввод-вывод, так как он задает последовательность естественным образом).
Или continuation-passing.
Или непродолжение в случае ошибки в одном из предыдущих вычислений.
Или вывалить в виде списка.
В общем, что душе угодно.

То, как именно осуществляется эта связь, кодируется в функции bind. Что ты там накодируешь — такая монада и получится.
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  
Дата: 26.10.14 19:28
Оценка:
Здравствуйте, dimgel, Вы писали:

D>Вот же, тремя ветками ниже уже с полгода обсуждают: http://rsdn.ru/forum/philosophy/5380969
Автор: dimgel
Дата: 03.12.13


Во-во, яркий пример того, о чем я и говорил. На первых 5 страницах — ни одного нормального объяснения.
Re[3]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 26.10.14 19:33
Оценка:
Здравствуйте, AlexRK, Вы писали:

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


D>>Вот же, тремя ветками ниже уже с полгода обсуждают: http://rsdn.ru/forum/philosophy/5380969
Автор: dimgel
Дата: 03.12.13


ARK>Во-во, яркий пример того, о чем я и говорил. На первых 5 страницах — ни одного нормального объяснения.


Вот такое объяснение тебе нормально?
http://rsdn.ru/forum/philosophy/5392303.1
Автор: jazzer
Дата: 11.12.13


А вообще — отсортируй ту ветку по оценкам, там куча объяснений на любой вкус.
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  
Дата: 26.10.14 19:34
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Это способ связать вычисления каким-либо образом. Паттерн, если угодно. Выраженный прямо в языке.

J>Например, строго последовательное вычисление (сюда же и ввод-вывод, так как он задает последовательность естественным образом).
J>Или continuation-passing.
J>Или непродолжение в случае ошибки в одном из предыдущих вычислений.
J>Или вывалить в виде списка.
J>В общем, что душе угодно.

А зачем это нужно? Насколько мне известно, монады есть далеко не во всех даже функциональных языках.
Ввод-вывод можно и безо всяких монад выполнять последовательно в том порядке, в каком он записан.
Есть ли смысл в монадах для императивных языков?
Re[4]: Монады
От: AlexRK  
Дата: 26.10.14 19:38
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Вот такое объяснение тебе нормально?

J>http://rsdn.ru/forum/philosophy/5392303.1
Автор: jazzer
Дата: 11.12.13


Да, это мне понятно. Но непонятно, как может выглядеть реализация этого.

Это похоже на "ты пишешь код, как будто дедлоков нет, но компилятор все отслеживает и не дает скомпилировать программу, если обнаружит дедлоки". То есть звучит все круто, но как это сделано?
Re[3]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 26.10.14 19:40
Оценка: 63 (3) +1
Здравствуйте, AlexRK, Вы писали:

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


J>>Это способ связать вычисления каким-либо образом. Паттерн, если угодно. Выраженный прямо в языке.

J>>Например, строго последовательное вычисление (сюда же и ввод-вывод, так как он задает последовательность естественным образом).
J>>Или continuation-passing.
J>>Или непродолжение в случае ошибки в одном из предыдущих вычислений.
J>>Или вывалить в виде списка.
J>>В общем, что душе угодно.

ARK>А зачем это нужно? Насколько мне известно, монады есть далеко не во всех даже функциональных языках.

ARK>Ввод-вывод можно и безо всяких монад выполнять последовательно в том порядке, в каком он записан.
1. Уменьшить писанину
2. У функциональных языках нет никакого другого способа сделать именно последовательное выполнение, кроме вложенного вызова (очевидно, что надо выполнить вложенный вызов, прежде чем использовать его результат в вызывающей функции). Вот чтоб все эти вложенные вызовы не писать многоэтажно, придумали специальный конструкт — монада, где все это будет спрятано.
(ввод-вывод — это просто пример)

ARK>Есть ли смысл в монадах для императивных языков?


Ну вот я рядом привел пример
Автор: jazzer
Дата: 11.12.13
автоматической проверки errno и прерывания исполнения функции, если что не так — стандартная сишная задача. Если бы были монады — можно было бы спрятать это внутрь оной и код стал бы на порядок чище.
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[5]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 26.10.14 19:46
Оценка:
Здравствуйте, AlexRK, Вы писали:

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


J>>Вот такое объяснение тебе нормально?

J>>http://rsdn.ru/forum/philosophy/5392303.1
Автор: jazzer
Дата: 11.12.13


ARK>Да, это мне понятно. Но непонятно, как может выглядеть реализация этого.


ARK>Это похоже на "ты пишешь код, как будто дедлоков нет, но компилятор все отслеживает и не дает скомпилировать программу, если обнаружит дедлоки". То есть звучит все круто, но как это сделано?


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

То есть всегда, когда у тебя в обычном языке есть несколько инструкций, которые друг с другом как-то связаны, и эта связь выражена одинаково (как в случае с errno) — ты можешь кишки, которыми закодирована эта связь, убрать с глаз долой внутрь монады. Независимо от характера связи. Вот и все.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Отредактировано 26.10.2014 19:49 jazzer . Предыдущая версия .
Re[4]: Монады
От: AlexRK  
Дата: 26.10.14 19:46
Оценка:
Здравствуйте, jazzer, Вы писали:

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


Если язык ленивый — то не факт, что вложенный вызов будет исполнен раньше, чем невложенный.

J>Ну вот я рядом привел пример
Автор: jazzer
Дата: 11.12.13
автоматической проверки errno и прерывания исполнения функции, если что не так — стандартная сишная задача. Если бы были монады — можно было бы спрятать это внутрь оной и код стал бы на порядок чище.


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

И еще я не пойму границ применимости этой концепции. Можно ли монадами сделать логгирование в начале и конце метода?
Re[5]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 26.10.14 19:56
Оценка: 8 (1)
Здравствуйте, AlexRK, Вы писали:

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


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


ARK>Если язык ленивый — то не факт, что вложенный вызов будет исполнен раньше, чем невложенный.


Как ты можешь получить результат выражения до того, как вычислил все его подвыражения (при условии, что все подвыражения используются, ессно)?
Если у тебя f()+g() — ты не можешь получить результат (сумму) раньше, чем будут вычислены оба подвыражения f() и g().
Ленивость лишь определяет, когда ты получишь и то, и другое, но порядок (иерархия) вычисления от ленивости не изменится.

J>>Ну вот я рядом привел пример
Автор: jazzer
Дата: 11.12.13
автоматической проверки errno и прерывания исполнения функции, если что не так — стандартная сишная задача. Если бы были монады — можно было бы спрятать это внутрь оной и код стал бы на порядок чище.


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


Да. И в Хаскеле тоже — там для этого есть специальная do notation, и она там, вроде как, тоже далеко не сразу появилась.

ARK>И еще я не пойму границ применимости этой концепции. Можно ли монадами сделать логгирование в начале и конце метода?


Монады относятся к организации вызовов. Так что логирование каждой строчки твоей функции (в функциональном языке это вызовы функций) — да. Или там профилирование.
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[4]: Монады
От: BrainSlug Израиль  
Дата: 26.10.14 19:58
Оценка:
J>1. Уменьшить писанину
J>2. У функциональных языках нет никакого другого способа сделать именно последовательное выполнение, кроме вложенного вызова (очевидно, что надо выполнить вложенный вызов, прежде чем использовать его результат в вызывающей функции). Вот чтоб все эти вложенные вызовы не писать многоэтажно, придумали специальный конструкт — монада, где все это будет спрятано.
J>(ввод-вывод — это просто пример)
это, конечно, так, но мне казалось придумали для декомпозиции вычислений, на pure и не pure.
.
Re[5]: Монады
От: Evgeny.Panasyuk Россия  
Дата: 26.10.14 20:14
Оценка: 8 (1)
Здравствуйте, AlexRK, Вы писали:

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

ARK>Если язык ленивый — то не факт, что вложенный вызов будет исполнен раньше, чем невложенный.

Там, грубо говоря, вывод делает не сам вызов, а его результат. И вот эти результаты как раз выстраиваются в жёсткую последовательность.
Если ты просто где-то сделаешь вызов с выводом, не передав его результат в соответствующую монаду, то никакого вывода не будет.

J>>Ну вот я рядом привел пример
Автор: jazzer
Дата: 11.12.13
автоматической проверки errno и прерывания исполнения функции, если что не так — стандартная сишная задача. Если бы были монады — можно было бы спрятать это внутрь оной и код стал бы на порядок чище.

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

Специальные синтаксические конструкции нужны только для удобства, например do-нотация. Монады-же можно использовать и без do-notation.
А вот один из вариантов того, как можно сделать do-нотацию в C++:
DO(Monad,
    (x, unit(1))
    (y, unit(2))
    (z, DO(Monad,
        (x, unit(5))
        (_, unit(x - 2))
    ))
    (_, unit(x + y + z))
)
Re: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 26.10.14 20:17
Оценка:
Здравствуйте, AlexRK, Вы писали:

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


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


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

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


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

P.S. Теоретически, это можно сделать и без монад. Практически — все равно получится монадическая сущность.
Re[6]: Монады
От: AlexRK  
Дата: 26.10.14 20:22
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Там, грубо говоря, вывод делает не сам вызов, а его результат. И вот эти результаты как раз выстраиваются в жёсткую последовательность.

EP>Если ты просто где-то сделаешь вызов с выводом, не передав его результат в соответствующую монаду, то никакого вывода не будет.

А... тогда понятно.

EP>Специальные синтаксические конструкции нужны только для удобства, например do-нотация. Монады-же можно использовать и без do-notation.

EP>А вот один из вариантов того, как можно сделать do-нотацию в C++:
EP>
EP>DO(Monad,
EP>    (x, unit(1))
EP>    (y, unit(2))
EP>    (z, DO(Monad,
EP>        (x, unit(5))
EP>        (_, unit(x - 2))
EP>    ))
EP>    (_, unit(x + y + z))
EP>)
EP>


Нда... По-моему, код с проверками выглядит гораздо чище, чем это.
Re[6]: Монады
От: Evgeny.Panasyuk Россия  
Дата: 26.10.14 20:22
Оценка:
Здравствуйте, jazzer, Вы писали:

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

ARK>>Если язык ленивый — то не факт, что вложенный вызов будет исполнен раньше, чем невложенный.
J>Как ты можешь получить результат выражения до того, как вычислил все его подвыражения (при условии, что все подвыражения используются, ессно)?
J>Если у тебя f()+g() — ты не можешь получить результат (сумму) раньше, чем будут вычислены оба подвыражения f() и g().
J>Ленивость лишь определяет, когда ты получишь и то, и другое, но порядок (иерархия) вычисления от ленивости не изменится.

Там не в этом дело, сам вызов вывод не производит. Его результирующие значение представляет действие по совершению этого вывода.

Например, для монад есть такой оператор >>:
putStrLn "abc" >> putStrLn "def"

Который реализуется как:
m >> n     =      m >>= \_ -> n

Тут, вообще говоря, n может быть вычислено раньше чем m.
Re[3]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 26.10.14 20:31
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>А зачем это нужно? Насколько мне известно, монады есть далеко не во всех даже функциональных языках.


Поддержка синтаксиса необязательна. хочешь хороший пример монады — посмотри Promise в джаваскрипте.

http://www.html5rocks.com/en/tutorials/es6/promises/

ARK>Ввод-вывод можно и безо всяких монад выполнять последовательно в том порядке, в каком он записан.

ARK>Есть ли смысл в монадах для императивных языков?

Есть. см ссылку выше. Еще пример — изолированая работа с состоянием.
В некоторых случаях вместо
stack.push(a)
...
stack.pop()


имеет смысл писать вот так —

var stack1 = push(stack0, a)
...
var (a,stackN) = pop(stackN-1)


Если хочется разобраться получше, начни с Promise в джаваскрипте и парсер-комбинаторов.
Re[4]: Монады
От: AlexRK  
Дата: 26.10.14 20:41
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Поддержка синтаксиса необязательна. хочешь хороший пример монады — посмотри Promise в джаваскрипте.

I>http://www.html5rocks.com/en/tutorials/es6/promises/

Посмотрел. Выглядит как ужасная грязь — даже на маленьких кусочках кода.
Страшно подумать, что будет на больших.

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

ARK>>Есть ли смысл в монадах для императивных языков?

I>Еще пример — изолированая работа с состоянием.

Это же вроде просто иммутабельные типы, нет?
Re[5]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 26.10.14 20:50
Оценка:
Здравствуйте, AlexRK, Вы писали:

I>>Поддержка синтаксиса необязательна. хочешь хороший пример монады — посмотри Promise в джаваскрипте.

I>>http://www.html5rocks.com/en/tutorials/es6/promises/

ARK>Посмотрел. Выглядит как ужасная грязь — даже на маленьких кусочках кода.

ARK>Страшно подумать, что будет на больших.

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

ARK>Лично мне нравится одно из двух: либо когда явно видно все, что происходит, пусть и многословно; либо когда абстракция полноценна и полностью скрывает все, что под капотом (например, асинхронный код выглядит и действует так же, как синхронный).


Промисы и есть многословно и прозрачно. При желании можно всё унутре спрятать.

ARK>>>Есть ли смысл в монадах для императивных языков?

I>>Еще пример — изолированая работа с состоянием.

ARK>Это же вроде просто иммутабельные типы, нет?


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

Когда найдешь способ, как же работать с таким стеком, ты изобретёшь монаду — отделение флоу от состояния.
Re[7]: Монады
От: Evgeny.Panasyuk Россия  
Дата: 26.10.14 20:53
Оценка:
Здравствуйте, AlexRK, Вы писали:

EP>>Там, грубо говоря, вывод делает не сам вызов, а его результат. И вот эти результаты как раз выстраиваются в жёсткую последовательность.

EP>>Если ты просто где-то сделаешь вызов с выводом, не передав его результат в соответствующую монаду, то никакого вывода не будет.
ARK>А... тогда понятно.

Продолжая тему монады ввод/вывод:

Есть функция putStrLn, которая принимает строку, и результатом которой является действие по выводу этой строки.
Эти действия можно выстроить в цепочку, с помощью того самого bind'а:
putStrLn "abc" >>= (\unused_parameter -> putStrLn "def")
(где \x -> f x — это лямбда с одним параметром, и вызывающая f для этого параметра)

Далее, есть функция getLine — которая возвращает действие по считыванию строки.
Доступ к считанной строке можно получить (и вывести с помощью putStrLn) через тот самый bind:
getLine >>= (\string_from_user -> putStrLn string_from_user)


Теперь рассмотрим пример чуть сложнее, нужно считать две строки, склеить и вывести:
getLine >>= 
    (\first_string ->
        getLine >>=
            (\second_string ->
                putStrLn first_string ++ second_string
            )
    )
Не очень удобно, правда?
Для упрощения синтаксиса и была введена do-нотация:
do
    first_string <- getLine
    second_string <- getLine
    putStrLn first_string ++ second_string
этот imperative-like код механически переписывается в предыдущий пример.

ARK>Нда... По-моему, код с проверками выглядит гораздо чище, чем это.


На C++ и код с проверками не нужен — можно использовать "встроенную монаду" исключение.
А вот пригодится такая нотация может, например, при работе с монадой List.
Re[5]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 26.10.14 20:59
Оценка:
Здравствуйте, AlexRK, Вы писали:

I>>Поддержка синтаксиса необязательна. хочешь хороший пример монады — посмотри Promise в джаваскрипте.

I>>http://www.html5rocks.com/en/tutorials/es6/promises/

ARK>Посмотрел. Выглядит как ужасная грязь — даже на маленьких кусочках кода.

ARK>Страшно подумать, что будет на больших.

Вот, можно упрятать грязь с помощью yield, это JS 6 — Harmony. Для запуска нужен например Node.js 0.11.х с параметром
--harmony

function main(){
    async(function* (){
        console.log('begin');
        console.log(yield sleep(100)); 
        console.log(yield sleep(200));
        console.log(yield sleep(300));
        console.log(yield sleep(400));
        console.log(yield sleep(500));
        console.log(yield sleep(600));
        console.log(yield sleep(700));
        console.log('end');
        yield sleep(800)
        return 'very end';
    }).then(function(value){
        console.log(value);    
    });
}

function async(coroutineFn){
    var coroutine = coroutineFn();

    function next(previous){
        var result = coroutine.next(previous);
        var current = Promise.resolve(result.value);

        if(result.done)
            return current;
        
        return current
                .then(function(value){
                    return next(value);
                });
    }

    return next(void 0);
}

function sleep(delay){
    return new Promise(function(succeed, failure){
        setTimeout(function(){
            succeed(delay);
        }, delay);
    });
}

main();
Re[6]: Монады
От: AlexRK  
Дата: 26.10.14 20:59
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Промисы и есть многословно и прозрачно. При желании можно всё унутре спрятать.


ИМХО, тут нельзя сказать, что все прозрачно. Выглядит как некий альтернативный поток исполнения, но, скорее всего, со своими приблудами.

ARK>>>>Есть ли смысл в монадах для императивных языков?

I>>>Еще пример — изолированая работа с состоянием.

I>Нет конечно. Попробуй реализовать абсолютно иммутабельный стек. Это никакая не монада.

I>Когда найдешь способ, как же работать с таким стеком, ты изобретёшь монаду — отделение флоу от состояния.

Ну я знаю, как делаются иммутабельные стеки. И даже очереди. Но причем тут монады, не пойму.
Re[7]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 26.10.14 21:04
Оценка:
Здравствуйте, AlexRK, Вы писали:

I>>Промисы и есть многословно и прозрачно. При желании можно всё унутре спрятать.


ARK>ИМХО, тут нельзя сказать, что все прозрачно. Выглядит как некий альтернативный поток исполнения, но, скорее всего, со своими приблудами.


Именно. Потому что это асинхронный код. С колбеками в общем случае проще не будет.

I>>Нет конечно. Попробуй реализовать абсолютно иммутабельный стек. Это никакая не монада.

I>>Когда найдешь способ, как же работать с таким стеком, ты изобретёшь монаду — отделение флоу от состояния.

ARK>Ну я знаю, как делаются иммутабельные стеки. И даже очереди. Но причем тут монады, не пойму.


Еще раз — иммутабельный стек это НЕ монада.

монада понадобится в том случае, когда ты захочешь заюзать такой стек вместо обычного и убрать весь лишний код.
Re[8]: Монады
От: AlexRK  
Дата: 26.10.14 21:05
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>[/haskell]Не очень удобно, правда?

EP>Для упрощения синтаксиса и была введена do-нотация:
EP>На C++ и код с проверками не нужен — можно использовать "встроенную монаду" исключение.

Я так понимаю, что Haskell героически борется с проблемами, которые сам себе и создал.
А кроме задания последовательности для ввода-вывода и исключений (которые в императивные языки встроены и воспринимаются как нечто само собой разумеющееся) есть какие-то вещи, которые могли бы пригодиться и в императивщине?
Re[8]: Монады
От: AlexRK  
Дата: 26.10.14 21:13
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Еще раз — иммутабельный стек это НЕ монада.

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

А, понятно.

ИМХО, тут надо не монады применять, а скорее те же макросы — как я понял, монады все равно привносят грязь в код своими "нотациями".
С моей точки зрения было бы гораздо лучше, если бы я пометил иммутабельный стек специальным атрибутом, а компилятор по этому атрибуту нагенерил нужный код. При этом в месте вызова _ничего_ бы не указывало на то, что стек иммутабельный.
Re[9]: Монады
От: Evgeny.Panasyuk Россия  
Дата: 26.10.14 23:10
Оценка:
Здравствуйте, AlexRK, Вы писали:

EP>>[/haskell]Не очень удобно, правда?

EP>>Для упрощения синтаксиса и была введена do-нотация:
EP>>На C++ и код с проверками не нужен — можно использовать "встроенную монаду" исключение.
ARK>Я так понимаю, что Haskell героически борется с проблемами, которые сам себе и создал.

Отчасти так и есть. Например есть монады для работы с изменяемыми переменными, массивами, циклами типа while и т.п.

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


Например монады для упрощения работы с асинхронностью, хотя и тут появляются некоторые аналоги типа await.
Из реально интересного — реализация software transactional memory на монадах. Хотя конечно можно и без явных монад.
Также монады полезны тем, что помогают локализовать те или иные эффекты. То есть взглянув только на сигнатуру функции, сразу понятно что она использует IO (не считая хаков типа unsafePerformIO), или кидает исключение и т.п.
Также интересный пример применения монад — парсинг, хотя опять таки реализуется и без монад (тот же Boost.Spirit).
Re[9]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 27.10.14 00:46
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>>[/haskell]Не очень удобно, правда?

EP>>Для упрощения синтаксиса и была введена do-нотация:
EP>>На C++ и код с проверками не нужен — можно использовать "встроенную монаду" исключение.

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

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

Ну вот в Си исключений нет, только возвращаемые значения (и глобальные переменные, которые зло).
И в Хаскеле исключений нет, там тоже только возвращаемые значения.
Но в Хаскеле есть монады, которые позволяют убрать под капот что угодно, включая проверку-с-остановкой возвращенных значений (причем это будет делаться автоматически и проигнорировать/забыть обработать будет невозможно).
А в Си тебе разве что текстовыми макросами придется пользоваться (а вот монады строго типизированы).

А, например, асинхронщины (через колбэки) и в С++ нет встроенной, не только в Си и Хаскеле. Но, опять же, в Хаскеле это можно убрать под монадный капот, а в Си/С++03 придется объявлять гору функций и всюду передавать указатели на них (в С++11 — лямбды, что улучшает локальность кода, но все равно все кишки наружу).

То есть монады позволяют убрать под капот многие стратегии вычислений и оставить только суть (как это делают, например, исключения, по сравнению с кодом, проверяющим возвращаемые значения).
Тем и полезны.
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[10]: Монады
От: Evgeny.Panasyuk Россия  
Дата: 27.10.14 02:05
Оценка:
Здравствуйте, jazzer, Вы писали:

J>А, например, асинхронщины (через колбэки) и в С++ нет встроенной, не только в Си и Хаскеле. Но, опять же, в Хаскеле это можно убрать под монадный капот, а в Си/С++03 придется объявлять гору функций и всюду передавать указатели на них (в С++11 — лямбды, что улучшает локальность кода, но все равно все кишки наружу).

J>То есть монады позволяют убрать под капот многие стратегии вычислений и оставить только суть (как это делают, например, исключения, по сравнению с кодом, проверяющим возвращаемые значения).
J>Тем и полезны.

Интересны примеры полезных монад, но для которых пока нет удобных аналогов в императивных языках. Для асинхронности есть coroutine/fiber/await/yield/etc.

И, кстати, если брать обобщённые сопроцедуры — call-with-current-continuation, то это даже более мощный инструмент чем do-нотация.
Так как для монадизации кода через do-нотацию его фактически нужно переписывать, а для монадизации через call/cc достаточно нескольких локальных изменений (либо вообще без изменений — зависит от языка). Пример вот тут
Автор: Evgeny.Panasyuk
Дата: 16.01.14
.
Re: Монады
От: uncommon Ниоткуда  
Дата: 27.10.14 03:38
Оценка:
Здравствуйте, AlexRK, Вы писали:

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


Недавно я тупо прочитал статью по монадам в википедии, и там всё очень даже хорошо и понятно описано. В кратце идею можно передать достаточно просто:

The general monad concept includes any situation where the programmer wants to carry out a purely functional computation while a related computation is carried out on the side.


А дальше уже это дело обвешивается деталями в зависимости от конкретного языка.
Отредактировано 27.10.2014 3:44 uncommon . Предыдущая версия .
Re[5]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 27.10.14 03:41
Оценка:
Здравствуйте, BrainSlug, Вы писали:

J>>1. Уменьшить писанину

J>>2. У функциональных языках нет никакого другого способа сделать именно последовательное выполнение, кроме вложенного вызова (очевидно, что надо выполнить вложенный вызов, прежде чем использовать его результат в вызывающей функции). Вот чтоб все эти вложенные вызовы не писать многоэтажно, придумали специальный конструкт — монада, где все это будет спрятано.
J>>(ввод-вывод — это просто пример)
BS>это, конечно, так, но мне казалось придумали для декомпозиции вычислений, на pure и не pure.

Ну есть же монады List, Identity... вполне pure в смысле отсутствия побочных эффектов.

А для чего их исторически придумали — хз. Вполне возможно, что и для того, что ты пишешь.
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[10]: Монады
От: AlexRK  
Дата: 27.10.14 06:55
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Например монады для упрощения работы с асинхронностью, хотя и тут появляются некоторые аналоги типа await.

EP>Из реально интересного — реализация software transactional memory на монадах. Хотя конечно можно и без явных монад.
EP>Также монады полезны тем, что помогают локализовать те или иные эффекты. То есть взглянув только на сигнатуру функции, сразу понятно что она использует IO (не считая хаков типа unsafePerformIO), или кидает исключение и т.п.
EP>Также интересный пример применения монад — парсинг, хотя опять таки реализуется и без монад (тот же Boost.Spirit).

Вот то-то и оно — все полезное и без монад уже реализовано.

У меня пока складывается впечатление, что монады могут быть заменены кодогенерацией — причем с лучшим результатом, без всяких нотаций и т.п.
Re[9]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 27.10.14 07:47
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>ИМХО, тут надо не монады применять, а скорее те же макросы — как я понял, монады все равно привносят грязь в код своими "нотациями".

ARK>С моей точки зрения было бы гораздо лучше, если бы я пометил иммутабельный стек специальным атрибутом, а компилятор по этому атрибуту нагенерил нужный код. При этом в месте вызова _ничего_ бы не указывало на то, что стек иммутабельный.

Так оно и есть. Проблема в том, что все монады в язык не всунешь, а скажем макрописатели из тех что ближе, исписались — было 50 контрибуторов, а стало полтора коммита в месяц.
Re[11]: Монады
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 27.10.14 09:51
Оценка: +1
Здравствуйте, AlexRK, Вы писали:

ARK>Вот то-то и оно — все полезное и без монад уже реализовано.


Один нюанс: оно все реализовано очень по-разному, а когда монады выражены через систему типов единообразно, можно писать универсальный код, работающий с произвольными монадами.
Re[11]: Монады
От: Evgeny.Panasyuk Россия  
Дата: 27.10.14 09:54
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>У меня пока складывается впечатление, что монады могут быть заменены кодогенерацией — причем с лучшим результатом, без всяких нотаций и т.п.


Ха, так можно взять голый C и сказать что любую недостающую фичу можно заменить кодогенерацией
Re: Монады
От: Pzz Россия https://github.com/alexpevzner
Дата: 27.10.14 09:58
Оценка:
Здравствуйте, AlexRK, Вы писали:

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


http://rsdn.ru/forum/philosophy/2340340
Автор: BulatZiganshin
Дата: 08.02.07
Re[12]: Монады
От: AlexRK  
Дата: 27.10.14 10:49
Оценка:
Здравствуйте, D. Mon, Вы писали:

ARK>>Вот то-то и оно — все полезное и без монад уже реализовано.

DM>Один нюанс: оно все реализовано очень по-разному, а когда монады выражены через систему типов единообразно, можно писать универсальный код, работающий с произвольными монадами.

Звучит хорошо, но хотелось бы конкретный пример.

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

Вот и сейчас я не очень пойму — какой реальный код может быть написан, чтобы единообразно работать с монадой "последовательность вызовов" и монадой "исключение"? Что этот код делает и зачем он нужен?
Re[12]: Монады
От: AlexRK  
Дата: 27.10.14 10:56
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

ARK>>У меня пока складывается впечатление, что монады могут быть заменены кодогенерацией — причем с лучшим результатом, без всяких нотаций и т.п.

EP>Ха, так можно взять голый C и сказать что любую недостающую фичу можно заменить кодогенерацией

Если кодогенерация более удобна в использовании, чем встроенные фичи — то почему бы и нет?
Я всегда считал, что встроенная фича должна быть на порядок проще и удобнее, чем невстроенная (иначе нафига ее встраивать?).
Кодогенерация проще, чем встроенные классы? Вряд ли (оставим вопросы гибкости за бортом). Проще, чем встроенные исключения? Тоже вряд ли.
А вот с встроенными монадами как-то все уже не очень очевидно.
Re[13]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 27.10.14 10:59
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Звучит хорошо, но хотелось бы конкретный пример.


ARK>Ситуация напоминает ситуацию с функциональным программированием в целом. Сколько я уже читал басен про то, как все там прекрасно. Доходишь до примера — на тебе функцию Фибоначчи. Приведите, блин, код чтения-записи в файлы, генерации веб-страниц, работы с БД.


Есть книжка Real World Haskell.
Есть статья dmz Динамическая Опердень на Haskell
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  
Дата: 27.10.14 11:01
Оценка:
Здравствуйте, Pzz, Вы писали:

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


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


Pzz>http://rsdn.ru/forum/philosophy/2340340
Автор: BulatZiganshin
Дата: 08.02.07


Нет, это явно не "простой язык".
Что такое линейные/уникальные типы я знаю, а все остальное мимо.

а дальше идут monad transformers, которые представляют не просто одну монаду, а способ добавить к монаде новую функциональность. т.е. ввам не придётся например выбирать между Error monad (прекразает вычисление при возбуждении ошибки), State monad, Parsing monad (сохраняет текущее состояние бибилиотеки парсинга), а вы их можете объединить вместе, наслоив одну на другую и использовать всю необходимую вам функциональность в общем коде. более того, вы можете добавить в этот бутерброд новые монады по мере развития программы, а эта запись do с вызываемыми в ней процедурами останется неизменной




По-хорошему, хотелось бы простых примеров, причем не на хаскелле, а на паскалеподобном псевдокоде (как в википедии алгоритмы описаны). Однако понимаю, что этого никто делать не будет.
Re[14]: Монады
От: AlexRK  
Дата: 27.10.14 11:05
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Есть книжка Real World Haskell.

J>Есть статья dmz Динамическая Опердень на Haskell

Хотелось бы разобраться с концепцией монад "вообще". Не касаясь языка Haskell никаким боком.
Re[15]: Монады
От: x-code  
Дата: 27.10.14 12:00
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Хотелось бы разобраться с концепцией монад "вообще". Не касаясь языка Haskell никаким боком.


Боюсь, что придется таки изучать этот Хаскель Я пытался разобраться с монадами без Хаскеля и даже что-то понял, но быстро забыл, так как в реальной практике этих монад нет, а без реальной практики все это слишком абстрактно для легкого изучения.
Re[16]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 27.10.14 12:08
Оценка:
Здравствуйте, x-code, Вы писали:

ARK>>Хотелось бы разобраться с концепцией монад "вообще". Не касаясь языка Haskell никаким боком.


XC>Боюсь, что придется таки изучать этот Хаскель Я пытался разобраться с монадами без Хаскеля и даже что-то понял, но быстро забыл, так как в реальной практике этих монад нет, а без реальной практики все это слишком абстрактно для легкого изучения.


Если только для изучения монад — Хаскель не нужен. Хватит джаваскрипта и его промисов.
Re[16]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 27.10.14 12:57
Оценка:
Здравствуйте, x-code, Вы писали:

ARK>>Хотелось бы разобраться с концепцией монад "вообще". Не касаясь языка Haskell никаким боком.


XC>Боюсь, что придется таки изучать этот Хаскель Я пытался разобраться с монадами без Хаскеля и даже что-то понял, но быстро забыл, так как в реальной практике этих монад нет, а без реальной практики все это слишком абстрактно для легкого изучения.


В реальной практике монады есть, только они хитро замаскировались — nullable, exceptions, yield, await, linq, stream

Особая практика нужна из за самого Хаскеля.
Re[17]: Монады
От: x-code  
Дата: 27.10.14 13:48
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>В реальной практике монады есть, только они хитро замаскировались — nullable, exceptions, yield, await, linq, stream


Тут сразу несколько аспектов.

1. Можно рассматривать каждую монаду как уникальный частный случай (ведь их не так много на самом деле даже в Хаскеле) в применении к императивным языкам — "какую бы еще фичу прикрутить к императивному языку Х?". В этом смысле каждая монада рассматривается специальная конструкция языка. Например if/else, циклы, try-catch, нуллабельность — это конструкции языка. Можно придумать что-то еще на основе малоизвестных монад и прикрутить, при этом пользователи даже не будут догадываться откуда все это.

2. Можно рассматривать монады как общую концепцию. С расчетом на то, что пользователь (т.е. программист) языка Х будет иметь возможность не только пользоваться готовыми монадами, но и создвать свои собственные. Это требует совершенно другого уровня понимания, и здесь нужно и изучать Хаскель, и смотреть как монады реализуются в других языках — причем желательно смотреть вообще на все начиная от Си и заканчивая Джаваскриптом. И медитировать над этим, пока не придет такое же чистое понимание монад, как большинство программистов понимают массивы или циклы. После этого можно попытаться придумать преемлемый для большинства синтаксис, не требующий понимания Хаскеля, и адекватный набор ключевых слов и языковых концепций, который достаточно красиво вписался бы скажем в синтаксис C# или Java, без костылей и "language features emulation", а так чтобы этим хотелось пользоваться.
Re: Монады
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.10.14 14:16
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Товарищи!


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


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


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


Монада это паттерн (то есть "узаконенная копипаста"). В отличие от всех остальных паттернов монады формализованы, то есть подчиняются некоторым математическим формулам.
Этот паттерн состоит из трех частей — монадного типа M<T> , функции bind и фукнции return. bind и return должны подчинаться трем монадным законам.

Основное предназначение паттерна — композиция функций T -> M<V>.
Таких функций на практике очень много:
1) M<T> — Future<T>\Task<T>, получаем механизм композиции асинхронных вычислений
2) M<T> — Option<T> — получаем механизм композиции вычислений, со значениями, которые могут отсуствовать
3) M<T> — IEnumerable<T> получаем механизм композиции последовательностей (списками)
...
Есть и более экзотические варианты M<T> — парсеры, значения с погрешностями, случайные величины и даже состояние, в том числе потокобезопасное.

Но сам по себе паттерн "монада" был бы не нужен никому, если бы не поддержка в языках.
Формальное определение паттерна позволяет в языки встроить "монадный синтаксис" (aka do-нотация, aka linq). Этот синтаксис позволяет вложенные вызовы bind записывать в линейной форме.

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

Еще одна фишка монад — дуальность. Имея некоторую монаду M, можно построить дуальную монаду M` просто "развернув стрелочки".

Вообще фишек у монад много, но они скорее интересны для академического изучения, а не для практического применения.
Re[2]: Монады
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 27.10.14 14:27
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Еще одна фишка монад — дуальность. Имея некоторую монаду M, можно построить дуальную монаду M` просто "развернув стрелочки".


Разворот стрелочек даст комонаду, совсем другую весчь. Полезных комонад в народном хозяйстве мало известно.
Re[13]: Монады
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 27.10.14 14:42
Оценка: +1
Здравствуйте, AlexRK, Вы писали:

ARK>>>Вот то-то и оно — все полезное и без монад уже реализовано.

DM>>Один нюанс: оно все реализовано очень по-разному, а когда монады выражены через систему типов единообразно, можно писать универсальный код, работающий с произвольными монадами.

ARK>Звучит хорошо, но хотелось бы конкретный пример.

ARK>Вот и сейчас я не очень пойму — какой реальный код может быть написан, чтобы единообразно работать с монадой "последовательность вызовов" и монадой "исключение"? Что этот код делает и зачем он нужен?

Ок, пример номер раз:
sequence :: Monad m => [m a] -> m [a]
Эта функция берет список из завернутых в некоторую монаду значений и возвращает завернутый в ту же монаду список уже очищенных значений. Например, в случае монады IO она из списка [readFile "a.txt", readFile "b.txt", readFile "c.txt"] сделает список ["a text", "b text", "c text"], завернутый в IO. А в случае монады исключения она список из [doSomething1, doSomething2, doSomething3] сделает либо список [result1, result2, result3] либо одно значение-исключение. И так с любой монадой.

Пример номер два:
msum :: MonadPlus m => [m a] -> m a
(где MonadPlus — это монада + моноид)
эта функция берет список завернутых в некоторую монаду значений из некоторого типа-моноида и сворачивает их в одно единственное значение (применением операции из моноида), завернутое в ту же монаду. Так, [readFile "a.txt", readFile "b.txt", readFile "c.txt"] превратится в строку, являющуюся конкатенированным содержимым указанных файлов, (завернутую в IO), а [doSomething1, doSomething2, doSomething3], где каждый из doSomething1 — это вычисление, производящее некоторое число или бросающее исключение, она превратит либо в сумму всех возвращенных значений (если все успешно вычислились), либо в значение-исключение, которое возникло при вычислении одного из них. Опять же, сама msum не знает заранее, с какой монадой будет работать.

Другие примеры тут
http://hackage.haskell.org/package/base-4.7.0.1/docs/Control-Monad.html
http://hackage.haskell.org/package/monad-loops-0.3.0.2/docs/Control-Monad-Loops.html
и т.д.

ARK>Ситуация напоминает ситуацию с функциональным программированием в целом. Сколько я уже читал басен про то, как все там прекрасно. Доходишь до примера — на тебе функцию Фибоначчи. Приведите, блин, код чтения-записи в файлы, генерации веб-страниц, работы с БД.


Да полно этого, не там читаете, видимо. Ту же Real World Haskell (доступна онлайн) тут уже упомянули.
Re[2]: Монады
От: AlexRK  
Дата: 27.10.14 14:44
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Этот паттерн состоит из трех частей — монадного типа M<T> , функции bind и фукнции return.


Как раз о чем я в первом посте и написал.

G>bind и return должны подчинаться трем монадным законам.


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

G>Но сам по себе паттерн "монада" был бы не нужен никому, если бы не поддержка в языках.

G>Формальное определение паттерна позволяет в языки встроить "монадный синтаксис" (aka do-нотация, aka linq). Этот синтаксис позволяет вложенные вызовы bind записывать в линейной форме.

А можно простой пример на псевдокоде? "Вот так — без монад, а вот так — с монадами, писанины в 3 раза меньше".
Re[14]: Монады
От: AlexRK  
Дата: 27.10.14 14:49
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Ок, пример номер раз:

DM>sequence :: Monad m => [m a] -> m [a]
DM>Эта функция берет список из завернутых в некоторую монаду значений и возвращает завернутый в ту же монаду список уже очищенных значений. Например, в случае монады IO она из списка [readFile "a.txt", readFile "b.txt", readFile "c.txt"] сделает список ["a text", "b text", "c text"], завернутый в IO. А в случае монады исключения она список из [doSomething1, doSomething2, doSomething3] сделает либо список [result1, result2, result3] либо одно значение-исключение. И так с любой монадой.

DM>Пример номер два:

DM>msum :: MonadPlus m => [m a] -> m a
DM>(где MonadPlus — это монада + моноид)
DM>эта функция берет список завернутых в некоторую монаду значений из некоторого типа-моноида и сворачивает их в одно единственное значение (применением операции из моноида), завернутое в ту же монаду. Так, [readFile "a.txt", readFile "b.txt", readFile "c.txt"] превратится в строку, являющуюся конкатенированным содержимым указанных файлов, (завернутую в IO), а [doSomething1, doSomething2, doSomething3], где каждый из doSomething1 — это вычисление, производящее некоторое число или бросающее исключение, она превратит либо в сумму всех возвращенных значений (если все успешно вычислились), либо в значение-исключение, которое возникло при вычислении одного из них. Опять же, сама msum не знает заранее, с какой монадой будет работать.

Вы взорвали мне мозг.

Я, увы (а может и не увы) Haskell не знаю (синтаксис понимаю очень поверхностно). Можно пример на императивном языке?
Re[15]: Монады
От: _NN_ www.nemerleweb.com
Дата: 27.10.14 15:03
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Здравствуйте, D. Mon, Вы писали:


DM>>Ок, пример номер раз:

DM>>sequence :: Monad m => [m a] -> m [a]
DM>>Эта функция берет список из завернутых в некоторую монаду значений и возвращает завернутый в ту же монаду список уже очищенных значений. Например, в случае монады IO она из списка [readFile "a.txt", readFile "b.txt", readFile "c.txt"] сделает список ["a text", "b text", "c text"], завернутый в IO. А в случае монады исключения она список из [doSomething1, doSomething2, doSomething3] сделает либо список [result1, result2, result3] либо одно значение-исключение. И так с любой монадой.

DM>>Пример номер два:

DM>>msum :: MonadPlus m => [m a] -> m a
DM>>(где MonadPlus — это монада + моноид)
DM>>эта функция берет список завернутых в некоторую монаду значений из некоторого типа-моноида и сворачивает их в одно единственное значение (применением операции из моноида), завернутое в ту же монаду. Так, [readFile "a.txt", readFile "b.txt", readFile "c.txt"] превратится в строку, являющуюся конкатенированным содержимым указанных файлов, (завернутую в IO), а [doSomething1, doSomething2, doSomething3], где каждый из doSomething1 — это вычисление, производящее некоторое число или бросающее исключение, она превратит либо в сумму всех возвращенных значений (если все успешно вычислились), либо в значение-исключение, которое возникло при вычислении одного из них. Опять же, сама msum не знает заранее, с какой монадой будет работать.

ARK>Вы взорвали мне мозг.


ARK>Я, увы (а может и не увы) Haskell не знаю (синтаксис понимаю очень поверхностно). Можно пример на императивном языке?


Императив это:

var a = GetUserInput();
var b = GetUserInput();
return a - b;


Функционально так просто нельзя потому как порядок неопределен
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Монады
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.10.14 15:17
Оценка: 6 (1)
Здравствуйте, AlexRK, Вы писали:

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


G>>Этот паттерн состоит из трех частей — монадного типа M<T> , функции bind и фукнции return.


ARK>Как раз о чем я в первом посте и написал.

А никакого тайного смысла нет, как и во всех других паттернах.

G>>bind и return должны подчинаться трем монадным законам.

ARK>Каким? Они не могут причинить вред программисту, должны ему подчиняться и заботиться о своей корректности?
1) bind(x, y => return y) = x
2) bind(return x, y => f(y)) = f(x)
3) bind(m, y => bind(g(y), x => f(x))) = bind(bind(m, x => f(x)), y => g(y))


G>>Но сам по себе паттерн "монада" был бы не нужен никому, если бы не поддержка в языках.

G>>Формальное определение паттерна позволяет в языки встроить "монадный синтаксис" (aka do-нотация, aka linq). Этот синтаксис позволяет вложенные вызовы bind записывать в линейной форме.

ARK>А можно простой пример на псевдокоде? "Вот так — без монад, а вот так — с монадами, писанины в 3 раза меньше".


Проще всего на примере Maybe монады увидеть
//Ручками
Maybe<int> Add1(Maybe<int> a, Maybe<int> b)
{
   if(!a.HasValue || !b.HasValue) return Maybe.None<int>();
   return Maybe.Return(a.Value+b.Value);
}

//Через bind
Maybe<int> Add2(Maybe<int> a, Maybe<int> b)
{
   return Bind(a, v1 => Bind(b, v2 => Maybe.Return(v1+v2)));
}

//Через монадный синтаксис
Maybe<int> Add3(Maybe<int> a, Maybe<int> b)
{
   return from v1 in a
          from v2 in b
          select v1+v2;
}


Последняя нотация вдвойне крута, ибо позволяет еще where использовать и локальные связывания (let).

Пока писал обратил внимание, что для написания первого варианта даже напрягся, проверяя логическое условие. Второй вариант потребовал усилия мозга, чтобы правильно расставить лямбды и скобки, а третий на автомате написал не задумываясь.
Re: Монады
От: HrorH  
Дата: 27.10.14 15:22
Оценка:
Здравствуйте, AlexRK, Вы писали:

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

Попробую своими словами, синтаксис C# + небольшие добавки для удобства.

Допустим, у нас есть какой-то тип X. И есть какие-то фукции, f1: X->X, f2: X->X.
И мы хотим уметь делать композицию этих функций. Мы определяем оператор <*>, чтобы можно было писать
f = f1 <*> f2

И еще мы хотим, что для функции Id, которая определена как x=>x , выполнялось

 f <*> id = f = id <*> f

То есть это по сути моноид.

А теперь мы хотим немножко усложнить, и научиться делать композицию фукнций вот такого вида: f1: A->M<B>, f2 : B->M<C>.
Делаем аналогично, добавляем операцию <*>.

Попробуем реализовать <*> (здесь return — это не функция, а ключевое слово C#)
<*> f1 f2 = 
    a =>  {
            var mb = f1 (a);
            return bind(mb, f2);
          };

Здесь я вызвал вспомогательную функцию bind. У каждой монады ее реализация своя.
Ее тип M<B>->(B->M<C>)->M<C>. Да, это и есть тот самый bind.

А теперь попробуем написать аналог f <*> id = f = id <*> f
f <*> return = f = return <*> f .

Тип функции return: T->M<T>. Слева это будет B->M<B>, а справа A->M<A>.

Если что непонятно или неправильно написал, более подробно и на английском
см Brian Beckman: Don't fear the Monad
Отредактировано 27.10.2014 15:58 HrorH . Предыдущая версия . Еще …
Отредактировано 27.10.2014 15:54 HrorH . Предыдущая версия .
Re[4]: Монады
От: x-code  
Дата: 27.10.14 19:06
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Последняя нотация вдвойне крута, ибо позволяет еще where использовать и локальные связывания (let).


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


Для меня как чистого императивщика первый вариант прост и естественен, даже не задумываясь написал бы именно такую проверку условий (каждый день пишу нечто подобное, и гораздо сложнее);

Второй вариант заставил напрячься, ибо откуда-то взялись имена "v1" и "v2". Потом понял что это C#, а v1 и v2 это аргументы лямбд... ну да, мудрено, вроде и код простой в одну строчку но мудрено. Функция Bind, которая принимает на вход другую функцию (первая лямбда), которая вызывает еще одну Bind, которая принимает еще одну функцию (вторая лямбда), которая вызывает некую "стандартную" функцию Return... По сигнатуре можно догадаться что Bind возвращает объект типа Maybe. Но вот как оно там во время выполнения раскручивается, в уме не представить... Ленивые вычисления именно что вывернуты по отношению к обычным императивным инструкциям, навреное тут надо иметь какую-то привычку. Многие начинающие программисты испытывают подобные сложности с восприятием рекурсии — думаю тут что-то схожее.

Третий вариант.. это та самая "do-нотация" про которую тут упоминают? Интересно она "ассоциируется" с LINQ.. никогда бы не подумал что это (монады и LINQ) из одной оперы. Конечно третий вариант понятнее. И любопытно было бы посмотреть на монады list или promise в таком виде...

Но, был бы еще понятнее четвертый вариант:
Maybe<int> Add3(Maybe<int> a, Maybe<int> b)
{
   return a+b;
}

и чтобы оно само все разрулилось как надо. На этапе компиляции, выполнения — не важно. Интересно, такое возможно?
Re[4]: Монады
От: AlexRK  
Дата: 27.10.14 19:22
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Проще всего на примере Maybe монады увидеть

G>
G>//Ручками
G>Maybe<int> Add1(Maybe<int> a, Maybe<int> b)
G>{
G>   if(!a.HasValue || !b.HasValue) return Maybe.None<int>();
G>   return Maybe.Return(a.Value+b.Value);
G>}

G>//Через bind
G>Maybe<int> Add2(Maybe<int> a, Maybe<int> b)
G>{
G>   return Bind(a, v1 => Bind(b, v2 => Maybe.Return(v1+v2)));
G>}

G>//Через монадный синтаксис
G>Maybe<int> Add3(Maybe<int> a, Maybe<int> b)
G>{
G>   return from v1 in a
G>          from v2 in b
G>          select v1+v2;
G>}
G>


Хм, но это же ад. Первый вариант — самый понятный.

Да и зачем монада тут?

Разве нельзя сделать примерно так (предположим, что в С# есть интерфейсы для чисел и классы типов):

T Add<T>(T a, T b) where T : IAddable<T>
{
   return a + b;
}

implementation IAddable<int> for Maybe<int>
{
  ...
}


И все — можно в этот Add пхать что угодно, хоть число, хоть Maybe...


Ну и самое главное — на этом примере уменьшения количества писанины не видно...
Re[5]: Монады
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.10.14 20:56
Оценка: 8 (1)
Здравствуйте, x-code, Вы писали:

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


G>>Последняя нотация вдвойне крута, ибо позволяет еще where использовать и локальные связывания (let).


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


XC>Для меня как чистого императивщика первый вариант прост и естественен, даже не задумываясь написал бы именно такую проверку условий (каждый день пишу нечто подобное, и гораздо сложнее);

А тем не менее это самый error-prone вариант. Так как на каждый метод, оперирующий Maybe<T> надо будет копипастить проверку на наличие значения, очень легко забыть\напутать\промазать.

XC>Второй вариант заставил напрячься, ибо откуда-то взялись имена "v1" и "v2". Потом понял что это C#, а v1 и v2 это аргументы лямбд... ну да, мудрено, вроде и код простой в одну строчку но мудрено. Функция Bind, которая принимает на вход другую функцию (первая лямбда), которая вызывает еще одну Bind, которая принимает еще одну функцию (вторая лямбда), которая вызывает некую "стандартную" функцию Return... По сигнатуре можно догадаться что Bind возвращает объект типа Maybe. Но вот как оно там во время выполнения раскручивается, в уме не представить... Ленивые вычисления именно что вывернуты по отношению к обычным императивным инструкциям, навреное тут надо иметь какую-то привычку. Многие начинающие программисты испытывают подобные сложности с восприятием рекурсии — думаю тут что-то схожее.

Да, вложенные лямбды очень сложны для чтения. Как и явная рекурсия. Эти два инструмента являются ассемблером функционального программирования.
Высокий уровень — это комбинаторы и do-нотация.



XC>Третий вариант.. это та самая "do-нотация" про которую тут упоминают?

В C# — да.

XC>Интересно она "ассоциируется" с LINQ.. никогда бы не подумал что это (монады и LINQ) из одной оперы.

Linq это и есть do-нотация в C#.
В частности код
   return from v1 in a
          from v2 in b
          select v1+v2;

При компиляции превращается примерно в такой:
return a.SelectMany(v1 => b.SelectMany(v2 => v1+v2))

То есть по сути превращается во второй вариант, без ручного выписывания лямбд.
Причем SelectMany может быть как членом класса, так и Extension методом — как определишь.
  дисклеймер
В C# чуть сложнее, потому что вложенные лямбды вырывают экспоненциальный рост сложности компиляции. Поэтому используется другая сигнатура SelectMany, но это к монадам отношения не имеет.


XC>Конечно третий вариант понятнее. И любопытно было бы посмотреть на монады list или promise в таком виде...

А что смотреть?
list-монада в C# это IEnumerable<T>, для которого как раз есть реализация linq в BCL.
promise (aka future) в .NET это Task<T>, для которых реализация linq легко гуглится — http://blogs.msdn.com/b/pfxteam/archive/2013/04/03/tasks-monads-and-linq.aspx


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

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

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

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

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


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

НО в haskell есть функция lift, которая позволяет "поднять" оператор в монаду.
Это страшное словосочетание говорит, что имея оператор T `x` V => U и монаду М (тип и две функции) автоматически преобразуется в M<T> `mx` M<V> => M<U>.
Теоретически в Haskell можно автоматом "поднимать" все операторы для всех монад в текущем контексте, практически мало пользы, ибо самое интересное не описывается арифметическими операторами.
Re[5]: Монады
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.10.14 21:08
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Хм, но это же ад. Первый вариант — самый понятный.

Понятность — очень субъективный параметр. Зависит от исключительно от предыдущего опыта писания.


ARK>Да и зачем монада тут?

Это же пример. В языке C#, кроме списков, монады практически не нужны.

ARK>Разве нельзя сделать примерно так (предположим, что в С# есть интерфейсы для чисел и классы типов)

Во-первых в C# таки нет классов типов, и не предвидится в ближайшее время.
Во вторых, запишем выражение по другому:
from v1 in a
from v2 in f(v1)
select g(v1, v2)

И никакие переопределения операторов не помогут разрулить.

ARK>Ну и самое главное — на этом примере уменьшения количества писанины не видно...

Странно что не видно...
Если ты не заметил, то для "ручного" варианта надо в каждом методе, оперирующим Maybe<T> проверять что есть значение (копипастить код).
Монады, как и другие паттерны, позволяют количество копипасты уменьшить и о паксимуму перенести в повторно используемые библиотеки.
Re[5]: Монады
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 28.10.14 06:42
Оценка:
Здравствуйте, x-code, Вы писали:

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

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

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

В Идрисе это вот так выглядит:
add : Maybe Int -> Maybe Int -> Maybe Int
add x y = [| x + y |]

Idiom brackets называется. Еще так можно:
add x y = return (!x + !y)
Re[6]: [offtolic]
От: jazzer Россия Skype: enerjazzer
Дата: 28.10.14 06:55
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>В Идрисе это вот так выглядит:

DM>
DM>add : Maybe Int -> Maybe Int -> Maybe Int
DM>add x y = [| x + y |]
DM>

DM>Idiom brackets называется. Еще так можно:
DM>
add x y = return (!x + !y)


Чем больше я смотрю на Идрис, тем больше Хаскель выглядит "на помоечку" — это так?
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]: [offtolic]
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 28.10.14 07:19
Оценка: 1 (1)
Здравствуйте, jazzer, Вы писали:

J>Чем больше я смотрю на Идрис, тем больше Хаскель выглядит "на помоечку" — это так?


Рановато пока. Чисто поиграться — Идрис конечно очень интересный и крутой, но на практике его пока сложно применять: очень медленный и не без глюков компилятор, тормозной код на выходе, скудная недокументированная стандартная библиотека (тоже не без багов), почти вакуум библиотек помимо стандартной.

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

И главное: нет гарантированной оптимизации хвостовых вызовов и нет другого способа сделать цикл. Т.е. если данных мало или повезло и хвостовой вызов где надо соптимизировался — работает, а чуть что — stack overflow и нет пути обойти. Авторам эта проблема известна, но они не спешат ее исправить (ибо весь кодогенератор перекурочивать надо), занимаются более академическими изысканиями.
Re[6]: Монады
От: AlexRK  
Дата: 28.10.14 08:12
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Понятность — очень субъективный параметр. Зависит от исключительно от предыдущего опыта писания.


Не могу согласиться. Объективно некоторые вещи простые, некоторые сложные.

G>Во вторых, запишем выражение по другому:

G>
G>from v1 in a
G>from v2 in f(v1)
G>select g(v1, v2)
G>

G>И никакие переопределения операторов не помогут разрулить.

Почему? Вот вариант без страхолюдства с select и from:
  g(a, f(a));


ARK>>Ну и самое главное — на этом примере уменьшения количества писанины не видно...

G>Странно что не видно...
G>Если ты не заметил, то для "ручного" варианта надо в каждом методе, оперирующим Maybe<T> проверять что есть значение (копипастить код).

Я заметил, что код точно так же копипастится и в двух других случаях — во втором два вызова Bind, в третьем два from-in. И в них точно так же легко ошибиться:
   return from v1 in a
          from v2 in a
          select v1+v2;


Опять же, оставив вопрос копипасты — количество кода с монадами меньше не стало.
Re[7]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 08:44
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Опять же, оставив вопрос копипасты — количество кода с монадами меньше не стало.


Не стало, но код в итоге получается более предсказуемый. И до кучи получается неявный pattern matching

В языке без поддержки монад все получается очень многословно, настолько, что профита никакого не будет. Но тем не менее есть вещи, где можно получить профит с монадами и в таком языке
Отредактировано 28.10.2014 8:56 Pauel . Предыдущая версия .
Re[5]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 08:53
Оценка: 8 (1)
Здравствуйте, AlexRK, Вы писали:

ARK>Разве нельзя сделать примерно так (предположим, что в С# есть интерфейсы для чисел и классы типов):

...
ARK>И все — можно в этот Add пхать что угодно, хоть число, хоть Maybe...

Если совсем правильно, то вот так

var a = b + c;


Это предельно просто — сложить два числа.

Если b и c это числа, то все просто.

Теперь фокус — b + c это значения "в будущем", то есть, результаты асинхронной операции. Связываем сейчас, в a, конечный результат получим потом.

Еще фокус — при вычислении b и c возможны ошибки. Тогда a так же будет с ошибкой. Ровно тот же подход — связываем сейчас, а конечный результат получим потом, но с ошибкой.

Далее, одно число вдруг окажется nullable, ничего не меняется — b + c

ARK>Ну и самое главное — на этом примере уменьшения количества писанины не видно...


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

реально трудно переопределить все возможные операторы, потому требуется поддержка языка.
На тех же промисах можно сделать так
var a = add(b, c)


снаружи нельзя будет сказать, что это за код, синхронный, асинхронный и тд. Выглядит совершенно одинаково.
Re[8]: Монады
От: AlexRK  
Дата: 28.10.14 08:59
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Не стало, но код в итоге получается более предсказуемый. И до кучи получается неявный pattern matching


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

Вот к примеру, если взять цикл "for (int i = 0; i < arr.Length; i++) { use(arr[i]; }" — здесь мы видим сразу 4 места, где можно ошибиться.
Путем замены цикла на "foreach (var item in arr) { use(item); }" мы ликвидируем все из них.

Или например, если мы это:
var
  val: Integer;
begin
  if condition then
    val = 3;
  else
    val = 5;
  use(val);
end


меняем на:
begin
  use(if condition then 3 else 5 end);
end


тоже преимущество видно и измеряемо: убрали лишнюю переменную.

А с монадами везде как-то шило на мыло получается. Я понимаю, что без монадных костылей функциональные языки не могут записать последовательность вызовов.
Но в императивном коде... Мне понравился пример jazzer'a про убирание цепочки if-ов, когда в языке нет исключений. Но опять же, ИМХО, такие вещи должны выглядеть на call-site без специальных нотаций и вообще без каких-то признаков наличия монад.
Re[6]: Монады
От: AlexRK  
Дата: 28.10.14 09:02
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Теперь фокус — b + c это значения "в будущем", то есть, результаты асинхронной операции. Связываем сейчас, в a, конечный результат получим потом.

I>Еще фокус — при вычислении b и c возможны ошибки. Тогда a так же будет с ошибкой. Ровно тот же подход — связываем сейчас, а конечный результат получим потом, но с ошибкой.
I>Далее, одно число вдруг окажется nullable, ничего не меняется — b + c

I>монады нужны дл того, что бы основную логику вытащить наружу, а исключения, время, состояние упрятать внутрь. То есть, что бы сложить два числа, нужно просто сложить два числа.

I>снаружи нельзя будет сказать, что это за код, синхронный, асинхронный и тд. Выглядит совершенно одинаково.

Да, вот это правильный вариант.
Интересно, можно ли этого достичь без метапрограммирования?
Re[15]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 09:04
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Вы взорвали мне мозг.


ARK>Я, увы (а может и не увы) Haskell не знаю (синтаксис понимаю очень поверхностно). Можно пример на императивном языке?


Здесь дело не в хаскеле, просто хаскелисты любят нагнать тумана.

Представь, ты пишешь простй линейный код, все просто, нет никаких чудес.

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

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

Теперь твой код, внезапно, стал особым способом оперировать состоянием. Опаньки — до кучи добавилась еще монада — состояние.
Re[9]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 09:13
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>А с монадами везде как-то шило на мыло получается. Я понимаю, что без монадных костылей функциональные языки не могут записать последовательность вызовов.


Попробуй свой цикл в примере сделать асинхронным, но сохранить последовательность.
Re[7]: Монады
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.10.14 09:18
Оценка:
Здравствуйте, AlexRK, Вы писали:

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


G>>Понятность — очень субъективный параметр. Зависит от исключительно от предыдущего опыта писания.


ARK>Не могу согласиться. Объективно некоторые вещи простые, некоторые сложные.


В том то и дело, что это субъективно.
Это как водить машину. Если ты всю жизнь ездил, то сесть за руль даже камаза не составит большого труда. Если нет, то будет казатся очень сложным.
Люди, которые всю жизнь пишут рекурсию считают циклы сложными, те кто пишут циклы — считают сложной рекурсию.


G>>Во вторых, запишем выражение по другому:

G>>
G>>from v1 in a
G>>from v2 in f(v1)
G>>select g(v1, v2)
G>>

G>>И никакие переопределения операторов не помогут разрулить.

ARK>Почему? Вот вариант без страхолюдства с select и from:

ARK>
ARK>  g(a, f(a));
ARK>

И в каждой функции f и g (и сотнях других) ручками выписать условие когда вернется None? Нет, спасибо.

ARK>>>Ну и самое главное — на этом примере уменьшения количества писанины не видно...

G>>Странно что не видно...
G>>Если ты не заметил, то для "ручного" варианта надо в каждом методе, оперирующим Maybe<T> проверять что есть значение (копипастить код).

ARK>Я заметил, что код точно так же копипастится и в двух других случаях — во втором два вызова Bind, в третьем два from-in. И в них точно так же легко ошибиться:

ARK>
ARK>   return from v1 in a
ARK>          from v2 in a
ARK>          select v1+v2;
ARK>

Вероятность опечатки от монад не зависит. А необходимость вписывать проверки очень даже завист.

ARK>Опять же, оставив вопрос копипасты — количество кода с монадами меньше не стало.

Еще раз: с монадой maybe тебе не надо в каждой функции проверять что значение есть, этот код будет один раз написан в bind.
Re[7]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 09:20
Оценка:
Здравствуйте, AlexRK, Вы писали:

I>>Теперь фокус — b + c это значения "в будущем", то есть, результаты асинхронной операции. Связываем сейчас, в a, конечный результат получим потом.

I>>Еще фокус — при вычислении b и c возможны ошибки. Тогда a так же будет с ошибкой. Ровно тот же подход — связываем сейчас, а конечный результат получим потом, но с ошибкой.
I>>Далее, одно число вдруг окажется nullable, ничего не меняется — b + c

I>>монады нужны дл того, что бы основную логику вытащить наружу, а исключения, время, состояние упрятать внутрь. То есть, что бы сложить два числа, нужно просто сложить два числа.

I>>снаружи нельзя будет сказать, что это за код, синхронный, асинхронный и тд. Выглядит совершенно одинаково.

ARK>Да, вот это правильный вариант.

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

Нужна или поддержка монад в языке, или метапрограммирование, или, на худой конец, динамическая типизация.
Re[7]: Монады
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.10.14 09:23
Оценка: +1
Здравствуйте, AlexRK, Вы писали:

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


I>>Теперь фокус — b + c это значения "в будущем", то есть, результаты асинхронной операции. Связываем сейчас, в a, конечный результат получим потом.

I>>Еще фокус — при вычислении b и c возможны ошибки. Тогда a так же будет с ошибкой. Ровно тот же подход — связываем сейчас, а конечный результат получим потом, но с ошибкой.
I>>Далее, одно число вдруг окажется nullable, ничего не меняется — b + c

I>>монады нужны дл того, что бы основную логику вытащить наружу, а исключения, время, состояние упрятать внутрь. То есть, что бы сложить два числа, нужно просто сложить два числа.

I>>снаружи нельзя будет сказать, что это за код, синхронный, асинхронный и тд. Выглядит совершенно одинаково.

ARK>Да, вот это правильный вариант.

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

Ты удивишься, но для "значений в будущем" будет уже знакомый тебе код:
from v1 in a
from v2 in b
select v1+v2



Реализация linq для Task<T> — http://blogs.msdn.com/b/pfxteam/archive/2013/04/03/tasks-monads-and-linq.aspx
Re[6]: Монады
От: dimgel Россия https://github.com/dimgel
Дата: 28.10.14 09:59
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>На тех же промисах можно сделать так

I>
I>var a = add(b, c)
I>


I>снаружи нельзя будет сказать, что это за код, синхронный, асинхронный и тд. Выглядит совершенно одинаково.


Now you don't know what you have. Could be an int, a string, a list or a problem.


(c)
Автор: jazzer
Дата: 04.07.11
Re[7]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 10:33
Оценка:
Здравствуйте, dimgel, Вы писали:

D>

D>Now you don't know what you have. Could be an int, a string, a list or a problem.


D>(c)
Автор: jazzer
Дата: 04.07.11


Примерно так, да
Re[8]: Монады
От: AlexRK  
Дата: 28.10.14 10:55
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>В том то и дело, что это субъективно.

G>Это как водить машину. Если ты всю жизнь ездил, то сесть за руль даже камаза не составит большого труда. Если нет, то будет казатся очень сложным.
G>Люди, которые всю жизнь пишут рекурсию считают циклы сложными, те кто пишут циклы — считают сложной рекурсию.

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

ARK>>Почему? Вот вариант без страхолюдства с select и from:

ARK>>
ARK>>  g(a, f(a));
ARK>>

G>И в каждой функции f и g (и сотнях других) ручками выписать условие когда вернется None? Нет, спасибо.

Нет, ничего выписывать не надо. Этот код должен быть абсолютно эквивалентен вашему с select-from.

G>Вероятность опечатки от монад не зависит. А необходимость вписывать проверки очень даже завист.


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

G>Еще раз: с монадой maybe тебе не надо в каждой функции проверять что значение есть, этот код будет один раз написан в bind.


В моем гипотетическом синтаксисе "g(a, f(a));" проверять тоже ничего не нужно.
Re[10]: Монады
От: AlexRK  
Дата: 28.10.14 10:57
Оценка:
Здравствуйте, Ikemefula, Вы писали:

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


ARK>>А с монадами везде как-то шило на мыло получается. Я понимаю, что без монадных костылей функциональные языки не могут записать последовательность вызовов.


I>Попробуй свой цикл в примере сделать асинхронным, но сохранить последовательность.


На него вполне можно смотреть как на асинхронный, если arr помечен соответствующим атрибутом (т.е. не вижу необходимости менять что-то в синтаксисе).
Re[9]: Монады
От: dimgel Россия https://github.com/dimgel
Дата: 28.10.14 10:57
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Если подавляющее большинство программистов считает нечто сложным, то оно, вероятно, и есть сложное.


Водка полезна. (c)
Re[8]: Монады
От: AlexRK  
Дата: 28.10.14 10:59
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Ты удивишься, но для "значений в будущем" будет уже знакомый тебе код:

G>
G>from v1 in a
G>from v2 in b
G>select v1+v2
G>


Неплохо. Осталось свести к нормальному синтаксису.
Re[8]: Монады
От: AlexRK  
Дата: 28.10.14 11:02
Оценка:
Здравствуйте, Ikemefula, Вы писали:

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

I>Нужна или поддержка монад в языке, или метапрограммирование, или, на худой конец, динамическая типизация.

А как, например, может выглядеть поддержка монад в языке? Особенно императивном. Как я понимаю, в Хаскелле этим термином именуется "do-нотация", но такой вариант мне не особо нравится. А других примеров в реальных языках, наверное, и не нет нигде?
Re[11]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 11:03
Оценка:
Здравствуйте, AlexRK, Вы писали:

I>>Попробуй свой цикл в примере сделать асинхронным, но сохранить последовательность.


ARK>На него вполне можно смотреть как на асинхронный, если arr помечен соответствующим атрибутом (т.е. не вижу необходимости менять что-то в синтаксисе).


А ты таки попробуй Без этого сложновато рассуждать про монады.
Re[12]: Монады
От: AlexRK  
Дата: 28.10.14 11:08
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>>>Попробуй свой цикл в примере сделать асинхронным, но сохранить последовательность.

ARK>>На него вполне можно смотреть как на асинхронный, если arr помечен соответствующим атрибутом (т.е. не вижу необходимости менять что-то в синтаксисе).
I>А ты таки попробуй Без этого сложновато рассуждать про монады.

Э... а что именно попробовать? Что у нас на входе, и что мы хотим на выходе?
Я так понимаю, что речь о каком-то таком цикле
foreach (var item in arr)
{
    use(item);
}

?
Re[9]: Монады
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.10.14 11:17
Оценка: +1
Здравствуйте, AlexRK, Вы писали:

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


G>>В том то и дело, что это субъективно.

G>>Это как водить машину. Если ты всю жизнь ездил, то сесть за руль даже камаза не составит большого труда. Если нет, то будет казатся очень сложным.
G>>Люди, которые всю жизнь пишут рекурсию считают циклы сложными, те кто пишут циклы — считают сложной рекурсию.

ARK>Я думаю, что статистика уверенно ответит на этот вопрос.

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

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

ARK>>>Почему? Вот вариант без страхолюдства с select и from:

ARK>>>
ARK>>>  g(a, f(a));
ARK>>>

G>>И в каждой функции f и g (и сотнях других) ручками выписать условие когда вернется None? Нет, спасибо.

ARK>Нет, ничего выписывать не надо. Этот код должен быть абсолютно эквивалентен вашему с select-from.

Каким образом? Maybe<T> может иметь значение Some(x) и None, при каждой операции надо проверять что внутри maybe. Код проверки надо скопировать во все фукнции, работающие с maybe. Сам он там не появился.
Монадический подход — завернуть эту проверку в bind и использовать обычные функции + do-нотацию (linq).

G>>Вероятность опечатки от монад не зависит. А необходимость вписывать проверки очень даже завист.

ARK>Вероятность опечатки зависит от количества мест, где ее можно сделать. Ваши примеры с монадами не сокращают количество таких мест.
Сокращает до одного — функции bind.


G>>Еще раз: с монадой maybe тебе не надо в каждой функции проверять что значение есть, этот код будет один раз написан в bind.

ARK>В моем гипотетическом синтаксисе "g(a, f(a));" проверять тоже ничего не нужно.

Прости, но давай реальность рассматривать, а не гипотетические языки.
Ибо в моем гипотетическом мире программы пишутся сами, а я только получаю деньги.
Re[10]: Монады
От: AlexRK  
Дата: 28.10.14 11:24
Оценка:
Здравствуйте, gandjustas, Вы писали:

ARK>>Вероятность опечатки зависит от количества мест, где ее можно сделать. Ваши примеры с монадами не сокращают количество таких мест.

G>Сокращает до одного — функции bind.

У bind есть лишние с точки зрения задачи параметры (v1 и v2).

ARK>>В моем гипотетическом синтаксисе "g(a, f(a));" проверять тоже ничего не нужно.

G>
G>Прости, но давай реальность рассматривать, а не гипотетические языки.

Ну я изначально имел в виду, что этот синтаксис гипотетический. То, что в C# так написать нельзя, я в курсе. Просто текущий синтаксис явно избыточен.
Кстати, думаю, что и в реальности есть языки, где можно так писать — какой-нибудь D.
Re[9]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 12:14
Оценка:
Здравствуйте, AlexRK, Вы писали:

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

I>>Нужна или поддержка монад в языке, или метапрограммирование, или, на худой конец, динамическая типизация.

ARK>А как, например, может выглядеть поддержка монад в языке? Особенно императивном.


Я ж вроде показал уже — http://rsdn.ru/forum/philosophy/5831962.1
Автор: Ikemefula
Дата: 26.10.14



yield это монада. аналогично монада await. можно более общий подход взять — computational expressions
В кратце — кое какие ключевые слова языка можно "перегрузить", почти как операторы:

Вот это поддержка сахаром
let maybe = new MaybeBuilder()
let sugared =
    maybe {
        let x = 12
        let! y = Some 11
        let! z = Some 30
        return x + y + z
    }


Это реальный выхлоп
let maybe = new MaybeBuilder()
let desugared = 
    maybe.Delay(fun () ->
        let x = 12
         maybe.Bind(Some 11, fun y ->
            maybe.Bind(Some 30, fun z ->
                maybe.Return(x + y + z)
                )
            )
        )


Взято отсюда http://en.wikibooks.org/wiki/F_Sharp_Programming/Computation_Expressions
Вообще в функциональщине всегда стоит сначала посмотреть в конец, тогда становится понятно, что в начале


Вот кстати, query comprehension — это тоже поддерка монад в языке. Можно прикрутить к чему угодно, но внятно выглядит только для запросов.

Я вот попробовал прикрутить query comprehension для парсинга, прикольно, но в целом идея так себе:

Фрагмент грамматики в РБНФ

middleIsRegex > '(' regex ')'
repetition    > (middleIsRegex | primitive) {'*'}


Реализация чз монады на query comprehension

            var middleIsRegex = from lp in lparen 
                                from rx in regex 
                                from rp in rparen
                                select rx;    

            var repeatedStar = from s in _.Repeat0(star) 
                               select s.HasValue ? s.Value[0] :Blank.New();

            var repetition = from rx in _.Or(middleIsRegex, primitive)
                             from rs in repeatedStar
                             select rs is AST.Blank ? rx : Repetition.New(rx);


Вот тот же фрагмент, монады но на функциях вместо query comprehension

            var middleIsRegex = _.Sequence(lparen, regex, rparen).Select(x => string.Concat(x));
            var regexOrPrimitive = _.Or(middleIsRegex, primitive);
            var repeatedStar = _.Repeat0(star).Select(toString);
            var repetition = _.Sequence(regexOrPrimitive, repeatedStar).Select(x => string.Concat(x));


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

В качестве бонуса query comprehension дает как бэ паттерн матчинг. Одно плохо — сходу непонятно, что унутре query. Для незнакомого человека функции более понятны.
Re[13]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 12:15
Оценка:
Здравствуйте, AlexRK, Вы писали:

I>>А ты таки попробуй Без этого сложновато рассуждать про монады.


ARK>Э... а что именно попробовать? Что у нас на входе, и что мы хотим на выходе?

ARK>Я так понимаю, что речь о каком-то таком цикле
ARK>
ARK>foreach (var item in arr)
ARK>{
ARK>    use(item);
ARK>}
ARK>

ARK>?

Представь, что функция use асинхронная, но надо сохранить фактический порядок.
Re[8]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 12:24
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>В том то и дело, что это субъективно.

G>Это как водить машину. Если ты всю жизнь ездил, то сесть за руль даже камаза не составит большого труда. Если нет, то будет казатся очень сложным.
G>Люди, которые всю жизнь пишут рекурсию считают циклы сложными, те кто пишут циклы — считают сложной рекурсию.

Очень интересно, особенно если посмотреть как дети возраста 6-7 лет осваивают инструкции и разные итерационные по своей природе вещи.

Циклам можно научить примерно с 3го класса. Рекурсии — не раньше, чем люди внятно функции освоят, т.е. примерно на 5 лет позже.
Re[10]: Монады
От: AlexRK  
Дата: 28.10.14 12:25
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>yield это монада. аналогично монада await. можно более общий подход взять — computational expressions

I>В кратце — кое какие ключевые слова языка можно "перегрузить", почти как операторы:

I>Вот это поддержка сахаром

I>
I>let maybe = new MaybeBuilder()
I>let sugared =
I>    maybe {
I>        let x = 12
I>        let! y = Some 11
I>        let! z = Some 30
I>        return x + y + z
I>    }
I>


I>Это реальный выхлоп

I>
I>let maybe = new MaybeBuilder()
I>let desugared = 
I>    maybe.Delay(fun () ->
I>        let x = 12
I>         maybe.Bind(Some 11, fun y ->
I>            maybe.Bind(Some 30, fun z ->
I>                maybe.Return(x + y + z)
I>                )
I>            )
I>        )
I>


I>Взято отсюда http://en.wikibooks.org/wiki/F_Sharp_Programming/Computation_Expressions

I>Вообще в функциональщине всегда стоит сначала посмотреть в конец, тогда становится понятно, что в начале

О, спасибо. Вот это мне уже понятнее.
Мало того, я даже кажется стал понимать, что к чему.
Бинд это типа для насаживания монад друг на друга, чтобы получить цепочку, а ретурн — вызывается, когда эту всю цепь надо вычислить.
Re[11]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 12:30
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>О, спасибо. Вот это мне уже понятнее.

ARK>Мало того, я даже кажется стал понимать, что к чему.
ARK>Бинд это типа для насаживания монад друг на друга, чтобы получить цепочку, а ретурн — вызывается, когда эту всю цепь надо вычислить.

Да, именно так.
Re[14]: Монады
От: AlexRK  
Дата: 28.10.14 12:30
Оценка:
Здравствуйте, Ikemefula, Вы писали:

ARK>>Э... а что именно попробовать? Что у нас на входе, и что мы хотим на выходе?

ARK>>Я так понимаю, что речь о каком-то таком цикле
ARK>>
ARK>>foreach (var item in arr)
ARK>>{
ARK>>    use(item);
ARK>>}
ARK>>

ARK>>?

I>Представь, что функция use асинхронная, но надо сохранить фактический порядок.


Наверное, в этом примере надо еще что-то дополнить, иначе все равно непонятно. Если "use" у нас внутри содержит побочные эффекты, то ее и асинхронно запускать нельзя. А если не содержит, то зачем нам порядок?
Re[15]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 28.10.14 12:47
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>>>Э... а что именно попробовать? Что у нас на входе, и что мы хотим на выходе?

ARK>>>Я так понимаю, что речь о каком-то таком цикле
ARK>>>
ARK>>>foreach (var item in arr)
ARK>>>{
ARK>>>    use(item);
ARK>>>}
ARK>>>

ARK>>>?

I>>Представь, что функция use асинхронная, но надо сохранить фактический порядок.


ARK>Наверное, в этом примере надо еще что-то дополнить, иначе все равно непонятно. Если "use" у нас внутри содержит побочные эффекты, то ее и асинхронно запускать нельзя. А если не содержит, то зачем нам порядок?


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

Предположим, use раз она асинхронная, то принимает колбек для завершения:
void use (T item, Action finished);


use(item[0],() => use(item[1],() => use(item[2],() => use(item[3],() => ... ))))


это можно свернуть в
items.Aggregate(()={}, (acc, item) => () => { use(item, acc); });


идейка так себе, хотя и рабочая. Цикл смотрится лучше

пусть use не вызывает колбек, а возвращает объект который связан с колбеком

Было
void use (T item, Action finished) {
   
  ... 
  xxx.onFinishedEvent = () => finished();
}


Стало

Future use (T item) {
  Future f = new Future(); 
  
 
  ... 
  xxx.onFinishedEvent = () => f.finished();
}


Можно вызвать по старому, сверткой
items.Aggregate(Finished.Empty, (acc, item) => { return acc.then(() => use(item, acc)); });


Тоже самое, что и выше, но теперь можно foreach всунуть, поскольку Future можно внятно yield-дать

forach(var item in items) {
  await use(item);
}


Теперь, если Future это Task, то получается всё как в дотнете
Re[16]: Монады
От: AlexRK  
Дата: 28.10.14 13:10
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Тоже самое, что и выше, но теперь можно foreach всунуть, поскольку Future можно внятно yield-дать


I>
I>forach(var item in items) {
I>  await use(item);
I>}
I>


I>Теперь, если Future это Task, то получается всё как в дотнете


Вроде понял. Спасибо!
Теперь самое время вернуться и прочитать топик с самого начала.
Re: Монады
От: maxkar  
Дата: 28.10.14 23:14
Оценка: 50 (4)
Здравствуйте, AlexRK, Вы писали:

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


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

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

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

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

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


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

Здесь же можно отметить, что у Box должен быть метод "упаковки" простого значения в контейнер. Например, так:
Box.box : T => Box<T>
//применение
val boxed : Box<Int> = Box.box(3)

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

Functor

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

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

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

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

Если вы внимательно посмотрите, это очень напоминает Promise. И это очень неудачный вариант предоставления функтора! Например, почти эквивалентно будет
class Box<T> {
  static def<T, R> map(fn : T => R, box : Box<T>) : Box<R>
}

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

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

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

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

Applicatives

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

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

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

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

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

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

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


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

Монады

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

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

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

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

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

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

Всё вместе

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

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

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

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


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

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

Плюсы этих "монад" для практики. Большую часть приложения все еще можно изобразить в чистых функциях. Их удобно писать и тестировать. Какая-то небольшая часть приложения работает с "грязными" данными (в виде асинхронных операций, текущего изменяемого состояния представленного в виде Behavior, etc...). Применение функций в этих классах грязных данных тоже оттестировано (functor, applicative, monad это 4 операции на каждый класс). В результате чего места для ошибок практически нет (не надо вручную все компоненты синхронизировать, например). Ну и вид программы более декларативный. Я объявляю те же компоненты интерфейса (и текущий вид) как функцию от входных данных и состояний, а не как набор методов по копированию всего подряд из модели в вид и обратно.
Re[2]: Монады
От: x-code  
Дата: 29.10.14 20:23
Оценка:
Здравствуйте, maxkar, Вы писали:

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


Вообще интересно... "Box" — это некая обертка вокруг значения. Это может быть дополнительная информация (option, either). Или несколько равноправных значений (list). Или вообще функция, которая это значение рассчитывает (асинхронное выполнение). Все это очень разные понятия.
Сама по себе концепция этой обертки уже очень интересна, без всяких монад. Есть ли какое-то общее определение этой "обертки" Что еще может быть оберткой?

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

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

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

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

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


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);


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

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>


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

В общем если не сложно прокомментируйте мои комментарии, хотя-бы да/нет
В любом случае спасибо, объяснение супер, завтра продолжу разбираться с остальной частью.
Отредактировано 29.10.2014 20:29 x-code . Предыдущая версия .
Re[11]: Монады
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.10.14 06:07
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>У меня пока складывается впечатление, что монады могут быть заменены кодогенерацией — причем с лучшим результатом, без всяких нотаций и т.п.

У меня пока складывается впечатление, что классы могут быть заменены кодогенерацией — причём с лучшим результатом, без всяких нотаций и т.п.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Монады
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.10.14 06:47
Оценка:
Здравствуйте, Ikemefula, Вы писали:
I>Циклам можно научить примерно с 3го класса. Рекурсии — не раньше, чем люди внятно функции освоят, т.е. примерно на 5 лет позже.
Странно. А у нас ханойские башни дети в садике перекладывали. Это примерно на 4 года раньше, чем третий класс.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[12]: Монады
От: AlexRK  
Дата: 30.10.14 07:04
Оценка:
Здравствуйте, Sinclair, Вы писали:

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


ARK>>У меня пока складывается впечатление, что монады могут быть заменены кодогенерацией — причем с лучшим результатом, без всяких нотаций и т.п.

S>У меня пока складывается впечатление, что классы могут быть заменены кодогенерацией — причём с лучшим результатом, без всяких нотаций и т.п.

Можете продемонстрировать, что будет достигнуто путем такой замены?

Для монад я выше уже показывал.
Re[4]: Монады
От: enji  
Дата: 30.10.14 08:36
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ну вот я рядом привел пример
Автор: jazzer
Дата: 11.12.13
автоматической проверки errno и прерывания исполнения функции, если что не так — стандартная сишная задача. Если бы были монады — можно было бы спрятать это внутрь оной и код стал бы на порядок чище.


Так а чем это в таком случае принципиально отличается от продвинутых макросов? Подозреваю в чем-то вроде немерла с использованием макросов можно сделать тоже самое.

Тут ниже приводят в пример linq, подозреваю он тоже реализуется на макросах...
Re[10]: Монады
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 30.10.14 10:37
Оценка:
Здравствуйте, Sinclair, Вы писали:

I>>Циклам можно научить примерно с 3го класса. Рекурсии — не раньше, чем люди внятно функции освоят, т.е. примерно на 5 лет позже.

S>Странно. А у нас ханойские башни дети в садике перекладывали. Это примерно на 4 года раньше, чем третий класс.

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

Рекурсия == абстркация. При чем очень высокого уровня. Внятные абстрации у детей появляются только лет после 12. В детском саду они только-только начинают появляться.

Если ты не согласен, можно проверить вместе — по скайпу например моим детям, они как раз такого возраста, как надо, а по успеваемости выше среднего в группе.

Дам 100$ если ты хотя бы одному объяснишь _рекурсию_.

Что касается циклов — примерно в 3м классе дети учатся выполнять инструкции вида "1 сделать 2 повторить"
Re[3]: Монады
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 30.10.14 12:01
Оценка: +1
Здравствуйте, x-code, Вы писали:

XC>Вообще интересно... "Box" — это некая обертка вокруг значения. Это может быть дополнительная информация (option, either). Или несколько равноправных значений (list). Или вообще функция, которая это значение рассчитывает (асинхронное выполнение). Все это очень разные понятия.

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

В общем случае это любое отображение Тип -> Тип (для которого можно реализовать упомянутые выше операции). Тут как раз полезно вспомнить исходное понятие из математики. Всякая монада — это прежде всего функтор, а функтор состоит из двух отображений: первое отображает объекты категории в объекты категории (в нашем случае — типы в типы), а второе отображает стрелки в стрелки (т.е. в нашем случае — функции в функции). Про второе (map) maxkar написал, про первое в явном виде забыл. Т.е. Box — это функция, отображающая тип-аргумент Т в некоторый другой тип. Это может быть класс Box<T>, а может быть функция String -> T, а может быть что-то еще, любой тип, зависящий от Т и удовлетворяющий нужным нам свойствам.

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


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

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


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

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


Потому что на самом деле map функтора — это отображение функций:
map : (A -> B) -> (Box<A> -> Box<B>)
что эквивалентно
map : (A -> B) -> Box<A> -> Box<B>
что, через uncurrying эквивалентно
map : ((A -> B) , Box<A>) -> Box<B>

И чаще всего map так и используется — не значения мапить, а функции.

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

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

Это тоже эквивалентные вещи. Если мы возьмем чистую фунцию
f : (A , B) -> C
что эквивалентно
f : A -> B -> C
Иฺ завернем ее в аппликативный функтор через return
return f : Box< A -> B -> C >
То сможем применить аппликацию из аппликативного функтора
<$> : Box <A -> B> -> Box<A> -> Box<B>
(return f) <$> (return a) : Box <B -> C>
(return f) <$> (return a) <$> (return b) : Box <C>
Re[5]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 30.10.14 12:49
Оценка: 2 (1)
Здравствуйте, enji, Вы писали:

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


J>>Ну вот я рядом привел пример
Автор: jazzer
Дата: 11.12.13
автоматической проверки errno и прерывания исполнения функции, если что не так — стандартная сишная задача. Если бы были монады — можно было бы спрятать это внутрь оной и код стал бы на порядок чище.


E>Так а чем это в таком случае принципиально отличается от продвинутых макросов? Подозреваю в чем-то вроде немерла с использованием макросов можно сделать тоже самое.


Вот такое лобовое приминение — ничем. (В этом смысле и функции можно на макросах реализовать)

Но монады (как и функции), в отличие от макросов, являются полноценными первоклассными объектами.
Они участвуют в системе типов, позволяя четко разграничивать код, завязанный на монады, и чистый код.
Они сами являются типами, т.е. для них возможно все, что возможно для типов.
Они могут скрещиваться друг с другом, давая монады более высокого порядка (например, монада future (в терминах С++11, т.е. асинхронное вычисление) может скрещиваться с монадой Maybe на случай, если вычисление бросит исключение и получение результата будет в принципе невозможным).
Для них даже есть Monad Comprehension (а List Comprehension становится просто тривиальным частным случаем, так как список — это тоже монада).
Для них можно писать функции, работающий с монадами непосредственно (так же, как можно писать функции, работающий с функциями — т.е. функции высшего порядка).

А дело все в том, что в функциональныхз языках ничего, кроме функций, по сути, нет. Зато есть очень широкие возможности по преобразованию функций и вызовов функций.
Функциональная программа — это просто гигантский вызов функции, которая "как-то" вызывает другие функции и далее до самого конца.
И фишка в том, что, так как ничего, кроме функций, нет, и все собирается из разной степени вывернутости вызовов функциями друг друга, можно вот упомянутым "как-то" управлять и преобразовывать то, как функции друг друга зовут. Этим и занимаются монады и подобные им конструкты.

А в императивных языках у тебя, кроме вызовов функций, есть еще императивные инструкции (циклы, условия, объявления переменных, и т.п). И если в достаточно продвинутом императивном языке ты на его функциональной части еще можешь навернуть нечто, похожее на монады, то вот с инструкциями ты ничего сделать не сможешь. Если только они сами не реализованы в функциональном стиле в виде макросов типа Немерла. Но такие макросы все равно будут "над" языком, в отличие от монад в функциональныхз языках, где они "внутри" и с ними можно работать.
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[6]: Монады
От: samius Япония http://sams-tricks.blogspot.com
Дата: 30.10.14 13:35
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Но монады (как и функции), в отличие от макросов, являются полноценными первоклассными объектами.

J>Они участвуют в системе типов, позволяя четко разграничивать код, завязанный на монады, и чистый код.
Разве код, завязанный на монады, грязный?
Re[7]: Монады
От: jazzer Россия Skype: enerjazzer
Дата: 30.10.14 13:47
Оценка: +1
Здравствуйте, samius, Вы писали:

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


J>>Но монады (как и функции), в отличие от макросов, являются полноценными первоклассными объектами.

J>>Они участвуют в системе типов, позволяя четко разграничивать код, завязанный на монады, и чистый код.
S>Разве код, завязанный на монады, грязный?
Загрязненный монадой, я имею в виду
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[8]: Монады
От: samius Япония http://sams-tricks.blogspot.com
Дата: 30.10.14 13:54
Оценка: :))
Здравствуйте, jazzer, Вы писали:

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


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


J>>>Но монады (как и функции), в отличие от макросов, являются полноценными первоклассными объектами.

J>>>Они участвуют в системе типов, позволяя четко разграничивать код, завязанный на монады, и чистый код.
S>>Разве код, завязанный на монады, грязный?
J>Загрязненный монадой, я имею в виду
и чистый от монады, соответственно? Тогда некоторым языкам повезло — они чисты как слеза младенца
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...
Пока на собственное сообщение не было ответов, его можно удалить.