Здравствуйте, alex_public, Вы писали:
_>Ну так такое без проблем реализуется в языках, в которых доступна перегрузка операторов. Только вот к монадам это не имеет никакого отношения.
Все реализуется на ассемблере. Вопрос в количестве телодвижений. Какой объем работы нужно проделать, чтобы реализовать все эти операции для optional<>, future<>, ...?
A>>Я, кнечно, могу ошибаться, но я не вижу переколбашивания. Если у T есть сложение, умножение и сравнение, то функция работает. Монада же дает эти операции "на лету" функций над базовым типом. _>Монада ничего подобного не даёт. Ну точнее она нам даёт автоматическую реализацию этих функций, но расставить их вызовы в коде надо руками.
Ты, наверное, имеешь ввиду расставить руками в Хаскеле. Так?
Но дело в том, что Монада -- математическая конструкция. Которая никак не привязана к Хаскелю и другим реализациям.
И ничего нам не помешает реализовать специальный язык Монаскель, в котором вручную ничего расставлять не придется, но мы огребем вохрох проблем
(и это только те, что вижу я).
Но даже сейчас, в Хаскеле, с помощью do-сахара нам, насколько я понял, не придется расставлять вызовы руками.
СУВ, akava
_>Поясняю разница в подходах: _>1. Монады. Определяется ровно одна функция и всё. Дальше, с помощью неё можно разрулить абсолютно все ситуации (в этом и фокус как бы). Но вставлять эту функцию по коду надо руками. _>2. Перегрузка операторов. Надо определить кучу операторов для каждого вида монады. И плюс возможно перегрузить несколько функций (те, которые определены не как шаблонные и соответственно не могут сами подхватить новый тип). Но зато после этого, никаких изменений в коде не потребуется вообще. Ни в одной букве.
Мы в философии. Мы можем фантазировать. Сделай, плиз, один шаг -- объедини 2 подхода.
Re[43]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Он короче, чем многословное решение на query comprehension. Решение на Хаскеле по краткости ты сильно вряд ли обгонишь, но я не могу его показать, Хаскелем не владею как то.
Да, скорее всего так и будет. Но у всего есть своя цена. И у Хаскеля она появляется в месте взаимодействия с реальным внешним миром...
I>Да, монады встроены в язык.
Да, в каком-то смысле можно так сказать. И естественно против таких "монад" вообще никто не может возражать. Т.е. упоминать их тут в контексте нашего обсуждения просто бессмысленно. А говорим мы именно о явных монадах, определённых программистом, и имеющих вполне конкретные общеизвестные свойства.
I>Чего ты хочешь объяснить, я уже не понимаю. Большей частью у тебя сравнение query comprehension С# vs императивный код на С++.
Вообще всё началось с того, что я попросил знатоков (в том смысле, что применяющих их где-то на практике) монад показать какие-нибудь примеры их полезного использования в мультипарадигменных языках, а не в Хаскеле (в котором без монад просто тоскливо).
Насколько я помню, практические единственным реальным примером были как раз твои реактивные монадные парсеры. Из плюсов надо отметить, что это действительно работающий пример. Ну а из минусов (что мы тут и обсуждали) — как мы увидели, тоже самое можно реализовать и без монад, причём частенько и ещё проще. Так что это хотя и был реальный ответ на мой вопрос, но он не стал аргументом в пользу монад.
Re[50]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, akava, Вы писали:
A>>Да, на протяжении всей цепочки, за счет вывода типов, все значения обернутся в монады. Но в конце цепочки, где мы будем использовать результат (вызов вэбсервиса, например) компилятор ругнется и скажет, что такая-то функция не умеет работать с типом optional<T>. ARK>Где-то может и уметь — совершенно случайно.
IQueriable в linq -- типичная монада.
В зависимости от реализации, она может и коллекцию итерировать, и в базу лезть, и вэбсервис вызывать, ... и эксепшены по делу и нет выкидывать. Это ведь никого не пугает.
Все, наоборот радуются, что один и тот же код может и "и коллекцию итерировать, и в базу лезть, и вэбсервис вызывать". А все это благодаря концепции Монада.
В свете вышесказанного, давай вернемся к ARK>Где-то может и уметь — совершенно случайно.
Если умеет -- отлично (если действительно умеет). Если не умеет, то наш тип (т.е. монада) обязана сделать все возможное, чтобы не ломать существующий код.
var q = (new []{1, 2, 3}).AsQueryable().Where(i=>i%2 == 0);
Console.WriteLine(q); // выводит: System.Int32[].Where(i => ((i % 2) == 0))
Вполне годное решение.
Хотя при работе с linq ты знаешь, что работаешь с linq. Т.е. должен понимать, что в зависимости от реализации IQueriable наш код будет вести себя по разному.
A>>Блин, чем больше в лес, тем дальше вылез
ARK>Да, я про это как раз и говорю. Но, судя по дальнейшему обсуждению, монады таки не предполагают волшебного изменения кода...
Конкретная реализация, конкретного языка не предполагает. Возможно есть языки, которые предполагают.
СУВ, aikin
Re[51]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Да, и выходит что самый оптимальный вариант получается в языке D, где мы можем переопределить все операторы для данного типа в одной функции. Если при этом ещё и все используемые функции являются шаблонными, то мы получаем полностью автоматическую замену T на optional<T> путём написания ровно одной функции и без модификации кода вообще.
Можно ссылку на это плиз?
СУВ, akava
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
S>По факту, в C# код Синклера никак не поменяется. Потому что ECMA 334:14.2.7 Lifted Operators S>В теории, хочется того же самого для пользовательских типов, а не только для Nullable<T>, оборудованного Compiler Magic.
Хы, ну для начала это очередная "встроенная монада" — такое нет смысла обсуждать в нашем контексте. Но что самое интересное, эта самая реализация "как бы лифтинга" как раз совсем не похожа на классическую, а гораздо больше напоминает обычную перегрузку операторов. ))) Т.е. я конечно не в курсе конкретной реализации, но есть подозрение, что там при реализации этих операторов, компилятор не вызывает каждый раз какую-то специальную функцию типа Nullable, а сам напрямую форвардит вызовы в T. Если это так, то это уже точно становится не явная монада, а просто некая отдалённо похожая концепция. Типа как исключения и т.п. — это вроде как тоже нечто напоминающее монады.
Re[37]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, AlexRK, Вы писали:
ARK>Я так понял, что речь идет не просто о чистых функциях, а о "выборочно чистых", к примеру чистых от работы с диском, но не от сетевых взаимодействий. В принципе, интересная идея, но в чем реальный бенефит выборочной чистоты и не будет ли это выглядеть адски страшно на любом ЯП — мне пока не ясно.
Хы, такими темпами можно дойти до введения "прав доступа" внутри языка. )))
Кстати, ООП со своей инкапсуляцией частично пытается решать эту проблему, причём с другой стороны. Не по принципу "проверим что эта функция не лезет куда не надо", а по принципу "код, который лезет куда не надо, нельзя вызвать ниоткуда, кроме спец. предусмотренных мест".
Re[51]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, akava, Вы писали:
A>Все реализуется на ассемблере. Вопрос в количестве телодвижений. Какой объем работы нужно проделать, чтобы реализовать все эти операции для optional<>, future<>, ...?
На D будет ровно по одной функции на каждый тип монад.
В C++ надо будет переопределить каждый оператор (сколько у нас их там?), но это можно сделать для шаблонного типа монад (т.е. в реализации вызывать некую функцию монады). Т.е. в итоге получаем по одной функции на каждый тип оператора и отдельно по одной функции для каждого типа монад.
В C# фокус из C++ не пройдёт и придётся определять заново все операторы для каждого типа монад.
Ну а в Хаскеле подобное вообще не сделать.
A>Ты, наверное, имеешь ввиду расставить руками в Хаскеле. Так?
Скорее просто про существующие языки. Ну во всяком случае общеизвестные. )))
A>Но дело в том, что Монада -- математическая конструкция. Которая никак не привязана к Хаскелю и другим реализациям. A>И ничего нам не помешает реализовать специальный язык Монаскель, в котором вручную ничего расставлять не придется, но мы огребем вохрох проблем
Не вижу особого смысла в создание подобного. )
A>Но даже сейчас, в Хаскеле, с помощью do-сахара нам, насколько я понял, не придется расставлять вызовы руками.
Эмм, так придётся расставлять собственно этот do-сахар. ))) Т.е. единственное его преимущество перед расстановкой просто bind в том, что он ставится сразу на некий блок кода (работающий с одними значениями), а не каждый вызов каждой функций/оператора в bind засовывать.
Re[52]: Есть ли вещи, которые вы прницпиально не понимаете...
Кстати, я тут подумал... Теоретически на D можно сделать даже и автоматические вызовы нешаблонных функций f(T) с типом optional<T>. Там же есть перегрузка операции opDispatch и плюс функция mixin... Т.е. грубо говоря, мы можем тривиально написать код (одну функцию!), который придаст нашему классу optional бесконечное число функций-членов, каждый из которых просто вызывает глобальную функцию с тем же именем, передавая ей внутреннее значение optional.
Мда, всё же в C++ ещё очень много чего надо добавить, чтобы добиться возможностей D. И как видно, на области метапрограммирования надо концентрироваться в первую очередь, т.к. она позволяет делать потом вообще что угодно.
Re[53]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Кстати, я тут подумал... Теоретически на D можно сделать даже и автоматические вызовы нешаблонных функций f(T) с типом optional<T>. Там же есть перегрузка операции opDispatch и плюс функция mixin... Т.е. грубо говоря, мы можем тривиально написать код (одну функцию!), который придаст нашему классу optional бесконечное число функций-членов, каждый из которых просто вызывает глобальную функцию с тем же именем, передавая ей внутреннее значение optional.
Накидал тут быстренько работающий пример:
double LibFunc(int v) {return 10.0*v;}
struct Optional(T){
this(const T v) {this.v=v; ok=true;}
string toString() {return ok?to!string(v):"nothing";}//для красоты примера, а вообще не требуетсяauto opBinary(string op, T2)(Optional!T2 t) {mixin Prepare!("v"~op~"t.v"); return ok&&t.ok?R(f()):R();}
auto opDispatch(string name)() {mixin Prepare!("v."~name~"()"); return ok?R(f()):R();}
private:
mixin template Prepare(string s) {const auto f=()=>mixin(s); alias Optional!(typeof(f())) R;};
bool ok=false;
T v;
};
auto Test(T)(T t1, T t2){return t1*t2+(t1+t2).LibFunc();}
writeln(Test(10, 20));
writeln(Test(Optional!int(10), Optional!int(20)));
writeln(Test(Optional!int(10), Optional!int()));
Данный код, как и полагается, выводит
500
500
nothing
и естественно все эти игры происходят исключительно во время компиляции и в итоге вырезаются оптимизатором.
Да, всё же D очень хорош, C++ заметно слабее. Ну а C# или Хаскель вообще даже рядом не валялись. )))
Re[50]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали: _>Хы, ну для начала это очередная "встроенная монада" — такое нет смысла обсуждать в нашем контексте.
Не вижу причин не обсуждать. Вы спрашивали "зачем нужны монады" — я вам показываю примеры, зачем нужны монады.
Волшебным образом оказывается, что монады и недомонады встречаются в "обычных языках" и библиотеках на каждом углу.
_>Но что самое интересное, эта самая реализация "как бы лифтинга" как раз совсем не похожа на классическую, а гораздо больше напоминает обычную перегрузку операторов. ))) Т.е. я конечно не в курсе конкретной реализации, но есть подозрение, что там при реализации этих операторов, компилятор не вызывает каждый раз какую-то специальную функцию типа Nullable, а сам напрямую форвардит вызовы в T.
Совершенно верно. Это compiler magic.
_>Если это так, то это уже точно становится не явная монада, а просто некая отдалённо похожая концепция. Типа как исключения и т.п. — это вроде как тоже нечто напоминающее монады.
Совершенно верно. Просто монады предлагают способ прикручивать поддержку исключений, Nullable, async, и прочего без compiler magic.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[48]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Эээ, что что? ) Насколько я понимаю, "главный" язык для работы с монадами у нас Хаскель. Так вот в нём компилятор не делает ничего подобного, а надо записывать всё руками.
Жаль. _>Ну для начала, если мы говорим о C++, то скажем все наши функции реализованные в виде шаблонов заработают сами автоматом. )))
Осталось придумать, как реализовать в виде шаблонов функции типа возведения в степень. Желательно так, чтобы они работали и для комплексных чисел тоже.
_>Ну а нешаблонные действительно надо оборачивать в Apply. Так же как и в Хаскеле (только там в bind). И кстати в Хаскеле это единственный вариант, в отличие от C++.
Как я понял, всё же в Хаскеле есть какие-то синтаксические улучшения для монад.
_>Ээээ что что? ) Это в каком языке компилятор так умеет? )
В ограниченном виде query comprehensions в C# делают именно это.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[38]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Хы, такими темпами можно дойти до введения "прав доступа" внутри языка. )))
_>Кстати, ООП со своей инкапсуляцией частично пытается решать эту проблему, причём с другой стороны. Не по принципу "проверим что эта функция не лезет куда не надо", а по принципу "код, который лезет куда не надо, нельзя вызвать ниоткуда, кроме спец. предусмотренных мест".
Совершенно верно. Логичным продолжением ограничений является "... ниоткуда, кроме спец. предусмотренных мест, в которые лазить нужно специальным способом".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
_>>Ну для начала, если мы говорим о C++, то скажем все наши функции реализованные в виде шаблонов заработают сами автоматом. ))) S>Осталось придумать, как реализовать в виде шаблонов функции типа возведения в степень. Желательно так, чтобы они работали и для комплексных чисел тоже.
Уже есть в SGI STL: <b>std::power</b>. Работает и для целых, и для вещественных, и для комплексных, и даже для матриц.
Power is generalized exponentiation: it raises the value x to the power n, where n is a non-negative integer.
_>>Ну а нешаблонные действительно надо оборачивать в Apply. Так же как и в Хаскеле (только там в bind). И кстати в Хаскеле это единственный вариант, в отличие от C++. S>Как я понял, всё же в Хаскеле есть какие-то синтаксические улучшения для монад.
Минимальные. Без синтаксических улучшений:
foo a b = a >>= \x ->
b >>= \y ->
return x + y
с do-сахаром:
foo a b = do x <- a
y <- b
return x + y
Re[50]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Уже есть в SGI STL: <b>std::power</b>. Работает и для целых, и для вещественных, и для комплексных, и даже для матриц.
Не вижу, каким образом оно работает для перечисленного: EP>
EP>Power is generalized exponentiation: it raises the value x to the power n, where n is a non-negative integer.
Важное выделено. Даже в банальную вещественную степень возвести эта штука не сможет.
EP>Минимальные. Без синтаксических улучшений: EP>
EP>foo a b = a >>= \x ->
EP> b >>= \y ->
EP> return x + y
EP>
с do-сахаром: EP>
EP>foo a b = do x <- a
EP> y <- b
EP> return x + y
EP>
Интересно. А при этом с "обычными", немонадными a и b такая запись работает? Ну, то есть можно ли вообще всегда описывать функции в do-нотации, чтобы они работали с произвольными монадами?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, akava, Вы писали:
A>Еще один косяк с операциями, возвращающими bool. Например сравнение.
A>
A>if (m > limit)
A> Alert();
A>if (!(m <= limit))
A> Alert();
A>
A>Что тут делать? Если мы определим '>' над optional<> как false, в случае когда o == null, то первая инструкция будет игнорировать null, а вторая сообщать о них.
Да, для bool операций в шарпе лифтинг сделан особенным образом.
A>Т.е. нужно будет об этом задумываться. А если нужно будет задумываться, то когда-нибудь, какой-нибудь разработчик Леша заменит первый вариант на второй (или наоборот) и изменит семантику кода. Вот это тоже КОСЯК.
Я бы заставил лифтед-операции возвращать bool?, который несовместим с "обычным" if-ом. Тогда компилятор мне все такие места сразу сообщит, и заставит руками выписать реакцию на unknown.
Это не так удобно, как в шарпе, зато единообразно — потому, что lifted operator > (IEnumerable<T> left, IEnumerable<T> right) должен вовращать таки IEnumerable<bool>, а не одиночный bool.
A>Товарищи опытные "монадоведы", расскажите плиз, что в таких случаях делать? Сразу хочется работать с монадоми вручную, без компилятора, но тогда монады теряют весь смысл. Единственное, что я смог придумать это принцип: если нужно значение из монады, то получай его явно (т.е. вручную).
Зависит от монады. Если оператор преобразования в базовый тип — implicit, то и хрен бы с ним. А в случаях типа optional или list или ещё чего всё должно быть строго explicit.
ARK>>Ну хз, может я слишком перестраховываюсь, ведь эти рассуждения справедливы и в других случаях (Int32 заменили на Int64, например). A>Я сам ХЗ. Не нужно воспринимать мои коментарии как "эгегей, монады куль, почему никто не использует монады...". Я сам только к ним присматриваюсь (как к новой для меня концепции). A>Аргументы выше, это то, что я сам себе ответил, когда задался таким вопросом.
Здравствуйте, alex_public, Вы писали:
K>>Ну так надо делать не "шаблоны", а нормальный параметрический полиморфизм. _>Чего чего? ) Это в смысле в рантайме?
Не понятно, что вы имеете в виду под "параметрическим полиморфизмом в рантайме" (я дискутировал с носителями самых фантастических представлений на этот счет, так что не провентилировав этот вопрос лучше даже и не начинать обсуждение).
_>Речь шла не про плюсы, а про гипотетический язык с шаблонами, в котором компилятор их полностью проверяет.
В гипотетическом языке можно сделать обязательные концепты и все будет типизировано, при том, что никакой "большой и лишней работы" тайпчекеру делать не придется.
_>Правильно, исходник по которому генерируется код. И только он, а не некие абстрактные исходники.
Под "исходниками по которым генерируется код" вы подразумеваете мономорфный код, сгенерированный по шаблонам? Ну так не нужно страдать ерундой и смешивать недопараметрический недополиморфизм и средства кодогенерации в одно непонятно что. Параметрический полиморфизм должен быть отдельно, а средства кодогенерации — макросы какие-нибудь — отдельно.
_>Я не собираюсь повторяться в третий раз. Даже дважды уже было много. Тем более, что в ответ не услышал даже одного раза.
Два раза много, а ноль раз, как в обсуждаемом случае — недостаточно. Повторяться не нужно — ответте на мои вопросы один раз. Этого будет достаточно.
_>Да да, конечно же совсем не признаёте... http://www.rsdn.ru/forum/philosophy/5407470
Вот об этом я и говорил. Обратите только внимание, что я ругаю околомонадный синтаксис и дурацкие системы именования, но не хвалю какие-то другие языки. Наоборот, рядом я написал следующее:
Я не согласен, что это выглядит намного страшнее, чем в других языках (если не считать упомянутые мной "карательные" наименования, которые легко исправить). Монадический код часто, но не всегда, требует синтаксической обвязки для "стыковки" в отличие от функций вида a -> b в хаскеле, и потому код с мутабельными ссылками/массивами может выглядеть многословнее, чем хаскель-код с иммутабельными. Но в большинстве языков обычно наворочено столько синтаксического мусора без всякого смысла вообще, что на этом фоне и монадический хаскель-код выглядит вполне пристойно. Потому и вопрос — что это за язык такой, по сравнению с которым хаскельный код выглядит намного страшнее.
Т.е. раз у хаскеля нерастраченный на всякую ерунду синтаксический бюджет — какие-то редкие уродства погоды не делают, особенно если сравнивать с языком, сыплющим всюду синтаксический мусор лопатой. Поскольку С++ как раз такой, код на нем даже в тех областях, где он силен смотрится страшнее, чем хаскель-код, демонстрирующий самые неприглядные стороны хаскеля.
Гипотетическую возможность сделать синтаксис лучше, чем в хаскеле я, разумеется, не отрицаю.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[44]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
I>>Чего ты хочешь объяснить, я уже не понимаю. Большей частью у тебя сравнение query comprehension С# vs императивный код на С++.
_>Вообще всё началось с того, что я попросил знатоков (в том смысле, что применяющих их где-то на практике) монад показать какие-нибудь примеры их полезного использования в мультипарадигменных языках, а не в Хаскеле (в котором без
монад просто тоскливо).
Спирит открой и удивляйся. Я как то не слежу за бустом и С++, а то бы раньше показал этот пример.
_>Насколько я помню, практические единственным реальным примером были как раз твои реактивные монадные парсеры. Из плюсов надо отметить, что это действительно работающий пример. Ну а из минусов (что мы тут и обсуждали) — как мы увидели, тоже самое можно реализовать и без монад, причём частенько и ещё проще. Так что это хотя и был реальный ответ на мой вопрос, но он не стал аргументом в пользу монад.
Promise/Future ты забыл ?
Re[51]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
S>Интересно. А при этом с "обычными", немонадными a и b такая запись работает? Ну, то есть можно ли вообще всегда описывать функции в do-нотации, чтобы они работали с произвольными монадами?
Чтоб такой код работал с обычными значениями, их придется обернуть в монаду Identity:
-- код был с ошибкой, правильно его записать так:
foo a b = do x <- a
y <- b
return $ x + y
-- или, что то же самое, но идиоматичнее:
foo = liftM2 (+)
-- теперь
runIdentity $ foo (Identity 2) (Identity 40)
-- получаем:
42
Это вполне применяется на практике, к примеру, все операции в пакете vector (это хаскельная библиотека для работы с массивами) монадические, а специализированная, нелифтнутые версии реализуются через монадические, вроде:
map f = mapM (return . f)
Lifted operators в хаскеле вполне реализуются:
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}import Control.Applicative
instance (Applicative f, Num n) => Num (f n) where
(+) = liftA2 (+)
(-) = liftA2 (-)
(*) = liftA2 (*)
abs = fmap abs
signum = fmap signum
fromInteger = pure . fromIntegral
instance (Applicative f, Fractional n) => Fractional (f n) where
(/) = liftA2 (/)
recip = fmap recip
fromRational = pure . fromRational
Все, теперь у нас арифметика лифтнута в любой аппликативный функтор и мы можем писать на языке j:
>>> 2 + 7 * 8
58
>>> Just 2 + Just 7 * Just 8
Just 58
>>> Just 2 + Nothing * Just 8
Nothing
>>> [1..3] + [7..9] * [11,12]
[78,85,89,97,100,109,79,86,90,98,101,110,80,87,91,99,102,111]
>>> getZipList $ ZipList[1..3] + ZipList[7..9] * ZipList[11..13]
[78,98,120]
-- функции - это тоже функторы и монады:
>>> sum / (fromIntegral . length) $ [1..3] -- вычисляем среднее значение: делим сумму на длину
2.0
Вообще, нормальная работа с функторами/монадами в хаскеле возможна, в основном, не благодаря спецсинтаксису (который как раз страшноватый), а благодаря приличным возможностям для написания полиморфного кода и комбинирования комбинаторов
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[63]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>>>Вообще спирит, судя по доке, построен на комбинаторах парсеров. Отсюда совершенно не ясно, как он может являться аргументом против монад, если сам он именно монаду и представляет. EP>>Комбинация парсеров — это не обязательно монада. Это может быть например аппликативным функтором (суперкласс монад, т.е. более общая структура). EP>>Чтобы называть монадой, нужно как минимум найти нетривиальный bind. I>Это ничего не меняет
В каком смысле? Ты хочешь называть Spirit монадой, но при этом не можешь показать где там bind (по смыслу)?
Re[51]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
S>Совершенно верно. Просто монады предлагают способ прикручивать поддержку исключений, Nullable, async, и прочего без compiler magic.
Конечно. Только вот в современных мультипарадигменных языках это всё (и ещё очень много чего) уже есть, причём как раз с полной магией. Т.е. мой вопрос насчёт полезности монад в таких языках по прежнему остаётся в силе...