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;


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