Здравствуйте, Klapaucius, Вы писали:
K>Все, теперь у нас арифметика лифтнута в любой аппликативный функтор и мы можем писать на языке j: K>
>>>> 2 + 7 * 8
K>58
>>>> Just 2 + Just 7 * Just 8
K>Just 58
>>>> Just 2 + Nothing * Just 8
K>Nothing
K>
Вот только тут нужны дополнительные оговорки, например:
cделать что-то типа if или PM не получится
foldl (+) 0 a (обсуждаемый выше) для [Maybe a] будет неэффективным
и т.п.
Если же в языке есть полноценный call/cc, то можно делать полноценные монадические вычисления не меняя код.
Подбое возможно например в C++, с помощью Boost.Coroutine — но не для всех типов монад (так как call/cc ограниченный).
Например:
template<template<typename> class M, typename T>
auto operator+(M<T> x, M<T> y)
{
return (await x) + (await y);
}
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, akava, Вы писали:
A>Согласен, что в реальности этот код должен поменяться, но мы в философии, поэтому можем пофантазировать. A>Например, предположим, что компилятор сам умеет делать из функции F функцию F* над optional<> аргументами. По принципу: все не null -> вычисляем F, что-то null -> результат null. A>Тогда код Синклера: A>
A>public T DoStuff<T>(List<T> array, T limit) {where T has '*' and '+' and '>'}
A>{
A> var sum = default(T); // вот тут у меня косяк, так как default(optional<T>) вернет null, но, уверен, он решаем
A> for(int i=0; i<array.length(); i++)
A> {
A> if (array[i]*array[i] > limit))
A> sum+=array[i]*array[i];
A> }
A> return sum;
A>}
A>
A>останется без изменений, потому как компилятор (см. выше) сделает нам сложение, умножение и сравнение на optional<> (сравнение по принципу если чт-то null, то false, как в sql).
A>Я, кнечно, могу ошибаться, но я не вижу переколбашивания. Если у T есть сложение, умножение и сравнение, то функция работает. Монада же дает эти операции "на лету" функций над базовым типом.
Это вполне реализуемо в языках с полноценным call-with-current-continuation, то есть код функции будет одинаковым для T, для AnyMonad<T>, etc.
Прямо сейчас это частично реализуемо в C++ на stackful coroutines. "Частично" потому что работает не для всех типов монад.
Re[49]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
S>Осталось придумать, как реализовать в виде шаблонов функции типа возведения в степень. Желательно так, чтобы они работали и для комплексных чисел тоже.
Ну если мы хотим реализовать всё одной функцией, то соответственно у нас должен быть универсальный алгоритм, оптимальный для любых типов. Если же для каких-то типов более оптимален другой алгоритм функции, то для этого в C++ есть явная специализация шаблонов.
S>В ограниченном виде query comprehensions в C# делают именно это.
Ну это вообще другая область. Скорее про лямбды, т.е. типа тех игр, которыми занимается Boost.Phoenix .
Re[51]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, akava, Вы писали:
A>IQueriable в linq -- типичная монада. A>В зависимости от реализации, она может и коллекцию итерировать, и в базу лезть, и вэбсервис вызывать, ... и эксепшены по делу и нет выкидывать. Это ведь никого не пугает. A>Все, наоборот радуются, что один и тот же код может и "и коллекцию итерировать, и в базу лезть, и вэбсервис вызывать". А все это благодаря концепции Монада.
То что один и тот же код может и по массиву ходить, и данные из базы таскать — это обычная абстракция+полиморфизм, а не монада.
(IQueryable действительно монада, за счёт SelectMany. Но не надо смешивать свойства полиморфизма и монады)
Re[64]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>>>Вообще спирит, судя по доке, построен на комбинаторах парсеров. Отсюда совершенно не ясно, как он может являться аргументом против монад, если сам он именно монаду и представляет. EP>>>Комбинация парсеров — это не обязательно монада. Это может быть например аппликативным функтором (суперкласс монад, т.е. более общая структура). EP>>>Чтобы называть монадой, нужно как минимум найти нетривиальный bind. I>>Это ничего не меняет
EP>В каком смысле? Ты хочешь называть Spirit монадой, но при этом не можешь показать где там bind (по смыслу)?
В том смысле что функторы спирита мало чем отличается от монад. Для данной беседы эта разница ничтожна.
Re[54]: Есть ли вещи, которые вы прницпиально не понимаете...
_>и естественно все эти игры происходят исключительно во время компиляции и в итоге вырезаются оптимизатором.
Супер! Но опять-таки, это не полноценные монадические вычисления — if(x > y) не сделать, accumulate по массиву Optional!int будет не оптимальным и т.п. Это больше applicative.
Чтобы это стало полноценной монадой, нужно прикрутить fiber'ы (но этот фокус сработает не для всех типов монад).
Re[65]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
EP>>В каком смысле? Ты хочешь называть Spirit монадой, но при этом не можешь показать где там bind (по смыслу)? I>В том смысле что функторы спирита мало чем отличается от монад. Для данной беседы эта разница ничтожна.
В каком смысле мало отличаются, тем что это тоже математическая структура?
То есть вопрос в том, нужны ли абстрактные структуры/концепции, которые обобщают поведения множества разных объектов?
Конечно же нужны
Re[66]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>>В каком смысле? Ты хочешь называть Spirit монадой, но при этом не можешь показать где там bind (по смыслу)? I>>В том смысле что функторы спирита мало чем отличается от монад. Для данной беседы эта разница ничтожна.
EP>В каком смысле мало отличаются, тем что это тоже математическая структура? EP>То есть вопрос в том, нужны ли абстрактные структуры/концепции, которые обобщают поведения множества разных объектов? EP>Конечно же нужны
Сравнивая с императивным кодом, что монады, что аппликативные функторы это одно и то же.
Re[51]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
EP>>Уже есть в SGI STL: <b>std::power</b>. Работает и для целых, и для вещественных, и для комплексных, и даже для матриц. S>Не вижу, каким образом оно работает для перечисленного:
Оно работает для любого моноида. Если немного поправить код, и убрать из начала:
if (n == 0)
return identity_element(opr);
то будет работать для любой полугруппы. Нужна всего лишь ассоциативная операция (например умножение матриц или конкатенация строк).
EP>>
EP>>Power is generalized exponentiation: it raises the value x to the power n, where n is a non-negative integer.
S>Важное выделено. Даже в банальную вещественную степень возвести эта штука не сможет.
Для вещественной степени будет больше требований к параметру.
А к чему вообще вопрос про power?
S>Интересно. А при этом с "обычными", немонадными a и b такая запись работает? Ну, то есть можно ли вообще всегда описывать функции в do-нотации, чтобы они работали с произвольными монадами?
Есть несколько вариантов — использовать Identity, как уже сказали выше, либо сделать все не-монады монадами (то есть с тривиальными return и >>=).
Re[37]: Есть ли вещи, которые вы прницпиально не понимаете...
K>Не понятно, что вы имеете в виду под "параметрическим полиморфизмом в рантайме" (я дискутировал с носителями самых фантастических представлений на этот счет, так что не провентилировав этот вопрос лучше даже и не начинать обсуждение).
Ну так параметрический полиморфизм же может иметь разные реализация, причём даже в рамках одного языка.
Самая эффективная естественно через обобщённое программирование (в случае C++ это шаблоны), т.к. всё отрабатывает на этапе компиляции.
Можно реализовать и в рантайме, через разные указатели на функции/данные (как уже удобно реализованный частный случай этого — ООП полиморфизм). Но это будет намного менее эффективно. Причём естественно не из-за одного лишнего уровня косвенности, а из-за невозможности компилятору заинлайнить это всё. Т.е. например люди не знакомые с этими нюансами и взглянувшие на внутренности скажем Boost'a и код генерируемый по нему были бы в шоке: огромные простыни сложного вложенного кода вырезаются компилятором начисто. А в случае реализации рантайм полиморфизма, это всё придётся оставить в бинарнике и исполнять при каждом вызове...
K>В гипотетическом языке можно сделать обязательные концепты и все будет типизировано, при том, что никакой "большой и лишней работы" тайпчекеру делать не придется.
Кстати про концепты... Они же уже на самом деле давно есть в C++, просто через некоторые "извращения". Но я не вижу какой-то принципиальной пользы от них ни сейчас, ни в удобном варианте.
Вот рассмотрим тот наш конкретный пример (монада в виде шаблонного типа). И предположим, что мы передаём вместо M какой-то неправильный тип, ну например вообще int. ))) В чём будет разница при наличие концептов и без них?
В том моём примере (без концептов), компилятор ругнётся приблизительно так: "шаблон liftM2<int> не может быть применён, т.к. у int не определён оператор >>=".
В случае с концептами компилятор ругнётся как-то типа "шаблон liftM2<int> не может быть применён, т.к. int — это не разновидность Monad".
Ну как бы и в чём существенное преимущество? ) Для меня оба эти сообщения об ошибке звучат приблизительно одинаково. Более того, первый вариант как бы сразу намекает как исправить ошибку.
K>Под "исходниками по которым генерируется код" вы подразумеваете мономорфный код, сгенерированный по шаблонам? Ну так не нужно страдать ерундой и смешивать недопараметрический недополиморфизм и средства кодогенерации в одно непонятно что. Параметрический полиморфизм должен быть отдельно, а средства кодогенерации — макросы какие-нибудь — отдельно.
Кстати говоря, тот факт, что шаблоны C++ можно использовать для довольно эффективного метапрограммирования, является скорее случайным побочным эффектом. А изначально это задумывалось исключительно как средство обобщённого программирования, т.е. как раз для параметрического полиморфизма.
_>>Да да, конечно же совсем не признаёте... http://www.rsdn.ru/forum/philosophy/5407470
)))
K>Вот об этом я и говорил. Обратите только внимание, что я ругаю околомонадный синтаксис и дурацкие системы именования, но не хвалю какие-то другие языки. Наоборот, рядом я написал следующее:
K>Т.е. раз у хаскеля нерастраченный на всякую ерунду синтаксический бюджет — какие-то редкие уродства погоды не делают, особенно если сравнивать с языком, сыплющим всюду синтаксический мусор лопатой. Поскольку С++ как раз такой, код на нем даже в тех областях, где он силен смотрится страшнее, чем хаскель-код, демонстрирующий самые неприглядные стороны хаскеля. K>Гипотетическую возможность сделать синтаксис лучше, чем в хаскеле я, разумеется, не отрицаю.
Так я в общем то и говорил всё время буквально это же самое, только более конкретизировано. Что Хаскель может быть удобнее в одной области и наоборот неудобнее в другой (при работе с внешним миром в частности). Естественно в сравнение с современными императивными языками, которые и заточены на максимальную эффективность во второй области. Ну и соответственно если взглянуть на соотношения размеров этих областей в обычном ПО, то выбор будет совсем не в пользу Хаскеля.
Это если говорить про синтаксис. Кроме этого, были ещё заявлены некоторые особенные возможности Хаскеля, которых нет в других языка, причём как раз в той самой второй области. Если от этих способностей действительно есть какая-то реальная польза на практике (а они не являются просто потребностью самого языка), то возможно вывод предыдущего абзаца следует пересмотреть. Но пока что я не видел ни единого примера на эту тему.
Re[51]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
S>Совершенно верно. Просто монады предлагают способ прикручивать поддержку исключений, Nullable, async, и прочего без compiler magic.
Да, и кстати... Можно прикручивать подобные вещи и без compiler magic и без монад, используя совсем другие возможности языка. Причём будет получаться гораздо эффективнее явных монад. Вот http://www.rsdn.ru/forum/philosophy/5427522
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP> cделать что-то типа if или PM не получится
Перегрузить все, чтоб немонадический код вообще без переделок делался монадическим конечно не получится. Но какой именно код с if и матчингом нужен?
EP> foldl (+) 0 a (обсуждаемый выше) для [Maybe a] будет неэффективным
Неэффективным по сравнению с чем? Если речь про раннее завершение (short circuit), то в нормальных языках такой проблемы нет как класса:
Здравствуйте, Ikemefula, Вы писали:
I>Спирит открой и удивляйся. Я как то не слежу за бустом и С++, а то бы раньше показал этот пример.
А где ты там монады нашёл? ) С большой натяжкой можно говорить об аппликативный функторах, и то сомнительно, т.к. значение над которым они все работают ни во что такое не упаковано. Это больше напоминает просто некое дерево функций.
I>Promise/Future ты забыл ?
Это в каком языке? )
Re[38]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Ну так параметрический полиморфизм же может иметь разные реализация, причём даже в рамках одного языка.
Может, конечно. Но я сейчас не о том, что может быть, а о том как это себе представляют.
_>Самая эффективная естественно через обобщённое программирование (в случае C++ это шаблоны), т.к. всё отрабатывает на этапе компиляции.
Что в данном случае понимается под "обобщенное программирование"? Кодогенерация?
_>Можно реализовать и в рантайме, через разные указатели на функции/данные (как уже удобно реализованный частный случай этого — ООП полиморфизм). Но это будет намного менее эффективно.
Вы смешиваете диспетчеризацию, которая может быть времени компиляции (как в случае параметрического полиморфизма с ограниченной квантификацией) и времени выполнения (как в случае экзистенциальных типов и виртуальных методов) и реализацию универсальной квантификации. Это, к сожалению, типичный случай. Давайте это как-нибудь разделим и обсудим поочередно или что-то одно, что вам интереснее.
_>Причём естественно не из-за одного лишнего уровня косвенности, а из-за невозможности компилятору заинлайнить это всё.
Вы всерьез считаете, что реализации параметрического полиморфизма для хаскеля или там sml не делают специализации и инлайна? Но в этом случае такая реализация была бы полностью бесполезна (из-за тормозов). Практичных подход заключается в том, чтоб программист мог сам выбирать трейдофф между скоростью работы скомпилированного кода и степенью раздельности компиляции. Т.е. сколько и каких разверток функций попадает в интерфейсный файл, чтоб потом специализировать/заинлайнить обобщенный код. Это проблема вполне решаемая и даже решенная.
_>Кстати про концепты... Они же уже на самом деле давно есть в C++, просто через некоторые "извращения". Но я не вижу какой-то принципиальной пользы от них ни сейчас, ни в удобном варианте.
_>Вот рассмотрим тот наш конкретный пример (монада в виде шаблонного типа). И предположим, что мы передаём вместо M какой-то неправильный тип, ну например вообще int. ))) В чём будет разница при наличие концептов и без них?
Смысл в том, чтоб обнаруживать ошибки до того, как какой-то конкретный int вообще откуда-то появится.
_>Кстати говоря, тот факт, что шаблоны C++ можно использовать для довольно эффективного метапрограммирования, является скорее случайным побочным эффектом. А изначально это задумывалось исключительно как средство обобщённого программирования, т.е. как раз для параметрического полиморфизма.
Это говорит только о сильно нетрадиционном понимании параметрического полиморфизма изобретателем шаблонов для плюсов и тем, что для прототипирования такой системы использовался кодогенератор-"шаблонизатор".
K>>у хаскеля нерастраченный на всякую ерунду синтаксический бюджет — какие-то редкие уродства погоды не делают, особенно если сравнивать с языком, сыплющим всюду синтаксический мусор лопатой. Поскольку С++ как раз такой, код на нем даже в тех областях, где он силен смотрится страшнее, чем хаскель-код, демонстрирующий самые неприглядные стороны хаскеля.
_>Так я в общем то и говорил всё время буквально это же самое, только более конкретизировано. Что Хаскель может быть удобнее в одной области и наоборот неудобнее в другой (при работе с внешним миром в частности). Естественно в сравнение с современными императивными языками, которые и заточены на максимальную эффективность во второй области. Ну и соответственно если взглянуть на соотношения размеров этих областей в обычном ПО, то выбор будет совсем не в пользу Хаскеля. _>Это если говорить про синтаксис.
Нет, это не то же самое, а противоположное. Перечитайте мое сообщение. Я пишу не о том, что код синтаксически лучше выглядит в какой-то другой области, а о том, что даже тот самый код, про который вы говорите "весь в большой монаде", если я, конечно, правильно понял, что вы имели в виду, синтаксически лучше, чем код на типичном императивном языке, синтаксис которого как будто придуман, чтоб покарать любого, кто захочет на этом языке что-то написать, не говоря уж о том, чтоб прочесть.
'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[54]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
EP>> cделать что-то типа if или PM не получится K>Перегрузить все, чтоб немонадический код вообще без переделок делался монадическим конечно не получится.
Получается что языки с сall-with-current-continuation в этом месте мощнее — можно обычный код сделать монадическим не меняя его.
K>Но какой именно код с if и матчингом нужен?
Да хотя бы:
f a = if a == 5 then"foo"else"bar"
допустим придёт IO Int.
EP>> foldl (+) 0 a (обсуждаемый выше) для [Maybe a] будет неэффективным K>Неэффективным по сравнению с чем? Если речь про раннее завершение (short circuit), то в нормальных языках такой проблемы нет как класса:
Я думаю можно сделать пример монады, где это не сработает (например добавить счётчик во внутрь монады, но нужно проверить законы моноида).
K>
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Супер! Но опять-таки, это не полноценные монадические вычисления — if(x > y) не сделать, accumulate по массиву Optional!int будет не оптимальным и т.п. Это больше applicative.
Ага. Я как раз это и демонстрирую, что задача решается эффективно и без монад.
Насчёт x>y мне кажется, что и не должно автоматом работать с монадой, потому как совершенно непонятно что тут должно происходит в реальном коде. Вот например для optional что там делать? Скорее такой код всё же надо править и как самый простейший вариант — обернуть в лямбду соответствующий кусок кода.
А что касается accumulate, то это как раз без проблем. Для этого в D надо просто перегрузить ещё один оператор для Optional: opAssign(string op)(r).
Re[46]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
I>>Спирит открой и удивляйся. Я как то не слежу за бустом и С++, а то бы раньше показал этот пример.
_>А где ты там монады нашёл? ) С большой натяжкой можно говорить об аппликативный функторах, и то сомнительно, т.к. значение над которым они все работают ни во что такое не упаковано. Это больше напоминает просто некое дерево функций.
Вот это "просто дерево функций" это без пяти минут монада. Что бы это стало полноценной монадо, всё что надо, это дописать две функции и по другому связывать. Все функции будут устроены ровно так же, один к одному.
При желании, описании грамматики ты сможешь сделать через эти функции, только смысла в этом мало, потому что нет поддержки со стороны компилятора.
I>>Promise/Future ты забыл ?
_>Это в каком языке? )
Во всех сразу. Пример тебе приводили.
Re[56]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>Супер! Но опять-таки, это не полноценные монадические вычисления — if(x > y) не сделать, accumulate по массиву Optional!int будет не оптимальным и т.п. Это больше applicative.
_>Ага. Я как раз это и демонстрирую, что задача решается эффективно и без монад.
"без монад" в данном случае означает структуру, которую до монады можно допилить ровно двумя функциями. Изначально у тебя "без монад" это просто императивный код.
Re[55]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Evgeny.Panasyuk, Вы писали:
K>>Перегрузить все, чтоб немонадический код вообще без переделок делался монадическим конечно не получится. EP>Получается что языки с сall-with-current-continuation в этом месте мощнее — можно обычный код сделать монадическим не меняя его.
Реальный язык или гипотетический?
EP>Да хотя бы: EP>
EP>f a = if a == 5 then"foo"else"bar"
EP>
допустим придёт IO Int.
Вообще-то if then else синаксис перегружаемый. На практике, правда, перегружать его никто не будет, да и вообще уместность его (if-a) применения в хаскеле достаточно спорная. Там скорее какой-нибудь комбинатор по случаю используют, который лифтится. Более серьезна проблема с ПМ, но и паттерн-матчинг — инструмент очень низкоуровневый, который лучше запереть в каком-нибудь комбинаторе.
EP>Я думаю можно сделать пример монады, где это не сработает (например добавить счётчик во внутрь монады, но нужно проверить законы моноида).
Если монада в принципе раннее завершение не поддерживает — конечно это работать не будет. Но весь смысл Maybe, в общем-то, в раннем завершении. Зачем она иначе нужна-то?
другой пример:
прибавляем двойку ко всем натуральным числам, смотрим первые пять получившихся:
Prelude Control.Applicative> take 5 $ 2 + [1..]
[3,4,5,6,7]
Монада List без раннего завершения тоже, в основном, бесполезная.
Они предназначены для того, чтоб комбинировать комбинаторы и получать в результате полезные функции. Без таких возможностей комбинации функций будут принципиально ущербными (на что вы сами и указали), ведь ручное их слияние всегда будет работать лучше. Естественно, получать бесполезные функции особого смысла нет, так что в энергичных языках комбинирование комбинаторов обычно сразу незадается.
EP>А как именно это реализовано (мемоизация, etc?) и есть ли гарантия этой оптимизации?
Это называется нормальный порядок редукции. Вовсе никакая не оптимизация. Я скопировал результаты из интерпретатора, а там оптимизаций нет. Оптимизация это как раз аппликативный порядок вычислений, когда для упрощения реализации идут на то, чтоб некий класс программ не работал вообще или работал плохо. Приведенный пример как раз из вторых. Если бы список был бесконечным — был бы вообще нерабочий (зацикливающийся).
'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[39]: Есть ли вещи, которые вы прницпиально не понимаете...
В C++ это шаблоны, в Java generics и т.п..
K>Вы смешиваете диспетчеризацию, которая может быть времени компиляции (как в случае параметрического полиморфизма с ограниченной квантификацией) и времени выполнения (как в случае экзистенциальных типов и виртуальных методов) и реализацию универсальной квантификации. Это, к сожалению, типичный случай. Давайте это как-нибудь разделим и обсудим поочередно или что-то одно, что вам интереснее.
Естественно речь про диспетчеризацию, т.к. именно она определяет эффективность (в смысле быстродействия).
K>Вы всерьез считаете, что реализации параметрического полиморфизма для хаскеля или там sml не делают специализации и инлайна? Но в этом случае такая реализация была бы полностью бесполезна (из-за тормозов). Практичных подход заключается в том, чтоб программист мог сам выбирать трейдофф между скоростью работы скомпилированного кода и степенью раздельности компиляции. Т.е. сколько и каких разверток функций попадает в интерфейсный файл, чтоб потом специализировать/заинлайнить обобщенный код. Это проблема вполне решаемая и даже решенная.
Я правильно понимаю, что нормальная оптимизация происходит только при условии, что обобщённый код и использующего его код находятся в одной единице компиляции?
Если да, то получается что никаких отличий от C++ нет — те же самые два варианта: нормальная оптимизация/совместная компиляция и плохая оптимизация/раздельная компиляция.
K>Смысл в том, чтоб обнаруживать ошибки до того, как какой-то конкретный int вообще откуда-то появится.
Ну так это тогда уже не к концептам и т.п., а к вообще другому языку, даже не знаю какому. Т.к. в языках с шаблонной техникой, шаблоны не трогаются пока они не используются.
K>Это говорит только о сильно нетрадиционном понимании параметрического полиморфизма изобретателем шаблонов для плюсов и тем, что для прототипирования такой системы использовался кодогенератор-"шаблонизатор".
Лично я считаю, что из всех существующих концепций в программирование, метапрограммирование — это наиболее сильный инструмент с любых точек зрения. И развивать в языке надо прежде всего его. Например C++ хорошо бы подтянуть хотя бы до уровня языка D.
K>Нет, это не то же самое, а противоположное. Перечитайте мое сообщение. Я пишу не о том, что код синтаксически лучше выглядит в какой-то другой области, а о том, что даже тот самый код, про который вы говорите "весь в большой монаде", если я, конечно, правильно понял, что вы имели в виду, синтаксически лучше, чем код на типичном императивном языке, синтаксис которого как будто придуман, чтоб покарать любого, кто захочет на этом языке что-то написать, не говоря уж о том, чтоб прочесть.
Ага, но саму разницу внутри Хаскеля тоже прекрасно ощущаете... Т.е. у нас одинаковое понимание пока мы смотрим только на Хаскель и разное, когда начинаем сравнивать его с другими языками.
Касательно сравнения монадного кода Хаскеля и кода на современных императивных языках. На мой взгляд тут довольно просто провести сравнение. Ведь если не вводить кучу уровней абстракции (а всё равно нижний уровень придётся писать), то подобный код сводится к последовательности вызовов некого системного API. Так вот на императивных языках это прямо буквально так и записывается. Ну а на монадах Хаскеля это записывается аналогично, плюс ещё некий набор ритуальных плясок... Разве не очевидно, какой вариант лучше? )