Здравствуйте, Ikemefula, Вы писали:
I>Теоретически, это достижимо и в С++, но на практике дл интеграции нужно специальное АПИ. Высокоуровневые вещи, внезапно, это вагоны темплейтов, конские иерархии и сложные макры, через которые очень трудно продираться. А вот аналогичное АПИ на динамичеком языке предельно простое. Вот мне непонятно, как работает pipe, открыл да посмотрел — смешное количество строчек кода. А в С++ только перчисление только темплейтов и скобочек будет раз в десять больше.
И да и нет. Смотря о чём говорить. Если говорить о смысловой части (разбиение задачи на функции, классы, алгоритмы вызовов и т.п.), то оно будет одинаковым на любом языке в исполнение одного программиста. А вот количество букв для записи этих классов и т.п. естественно разное. И в C++ оно безусловно больше, чем в обычных языках с динамической типизацией. Но этот объём является ценой за то, что значительная часть ошибок отсеивается ещё на этапе компиляции. Или даже вообще не появляется, благодаря возможностям современных IDE для подобных языков.
Re[65]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Нет, не одинаково. В спирите как видно, раза в три длинее и мутная нотация, мне например, было крайне сложно понять, что к чему
Ну давай запишем в одинаковом форматирование и без лишнего обрамление (ты бы ещё инклуды учёл), а только саму грамматику по честному.
auto const expr_def=term>>*((char_('+')>>term)|(char_('-')>>term));
auto const term_def=factor>>*((char_('*')>>factor)|(char_('/')>>factor));
auto const factor_def=uint_|'('>>expr>>')'|(char_('-')>>factor)|(char_('+')>>factor);
Что, прямо такая ужасная разница? )))
I>Это экономия на спичках. Никакого профита это не
Дело как раз не в экономии, а в другом. Хотя и с производительностью есть нюанс, т.к. при варианте Спирита можно вообще не выделять никакой памяти в процессе парсинга. Тут дело в том, что как раз при таком варианте происходит реально реактивный парсинг. Т.е. результаты выдаются сразу после прихода данных. Например, мы можем задать какой-то сложный шаблон и при этом получать результаты при поступление только части данных для него.
I>Дизайн в библиотеке я реализовал как мне было удобнее. В конечном итоге парсер может возвратить дерево, если его комбинаторы смапить на AST
Да не надо мне дерево. Ты мне покажи как мне написать функцию, которая будет хотя бы просто печатать разобранные данные. Вот для того же самого примера калькулятора пускай напечатает операторы другим цветом, а числа выведет в формате с плавающей точкой. А то у тебя пока все примеры были такие, что выдавали результат парсинга в виде некого одного предсказуемого результата (строки или числа там). А вот в каком виде мы получаем результат в случае если там некое выражение, состоящие из разных типов пока не понятно.
I>аналого-цифровой преобразователь ?
Ну да, это основной источник внешней информации вида непрерывной последовательности отсчётов в робототехнике и вообще автоматике. Соответственно, если твои тезисы верны, как раз работу с этим должны были первым делом перевести на всякие там реактивные монады и т.п. Однако я что-то ничего подобного не видел, кругом сплошные тупые императивные фильтры и анализаторы. Ну точнее я слышал краем уха про одну крайне сомнительную попытку (и даже запомнил название, т.к. оно другую библиотечку напоминало), но оно так и осталось теоретической забавой, которую никто и нигде применять не собирается. Вот оно https://www.cs.drexel.edu/~mainland/projects/flask/. А вообще, я прекрасно знаю как делаются подобные вещи сейчас в индустрии, и про функциональное программирование там народ обычно даже не слышал.
Re[39]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>При чем здесь сложность ? Ты раз за разом выдавал неправильное императивное решение и настаивал что оно работает.
Конечно работает. Если посмотрим на первый пример здесь на тему реактивных монадных парсеров (тот самый, что с заглавной страницы библиотечки Rxx), то я написал его ручной императивный аналог, работающий в реактивном режиме, меньше чем за минуту. Причём он оказался намного короче и намного проще для понимания. И вроде как ни у кого нет никаких претензий к его работе (придирки на счёт глобальной функции не принимаются — это тестовый пример и мы всегда можем одним движением заменить на объект, если надо завести много параллельных парсеров).
Но при этом я не раз повторял, что писать подобный код имеет смысл только в простых случаях (правда в этой темке что-то слишком часто попадались именно они). А в сложных следует использовать какие-то готовые библиотеки. Например конечные автоматы для каких-то событий или же Спирит, если говорим о тексте и т.п. Я надеюсь третьего (или уже четвёртого?) повтора этого моего тезиса будет достаточно, чтобы ты не "забыл" его снова? )
I>Если ты таки решил прикрутить короутины, то всё в порядке. Правда теперь не ясно, почему ты сопротивлялся столько дней.
Короутины+Спирит — это на самом деле совсем не оптимальное решение, т.к. там при работе на пустом месте происходит потеря производительности из-за переключений контекста. Оптимальное решение — это переписать Спирит под реактивный стиль. Но пока такого нет (а я точно не собираюсь делать подобное ради форумного спора ), то можно удовлетвориться и таким решением. Так сказать разумный компромисс между эффективностью кода и временем разработки.
I>Я ожидал что ты сразу переделаешь analyse на короутины, но мне уже надоело. Тебе теперь должно быть понятно, как императивно делается реактивный парсинг.
Короутины в Analyze? Зачем они там? Они могут понадобиться только если мы хотим заставить готовый библиотечный код (который мы не можем или не хотим менять) работать в асинхронном режиме. А если в Analyze только мой код, то больше ничего и не требуется.
I>Любой реактивный парсинг сожрёт ресурсы и выдаст фильтрованый результат только по окончании. И это не важно, с++ или асп.нет или yesod(хаскель).
С чего бы это? Ты не видел примеры в этой темке? )
Re[63]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Что касается сравнения производительности, то снова выходит сравнение нативного компилера с джытом. Это не интересно, для этого незачем городить парсеры.
Насчёт сравнения производительности согласен что не интересно. Хотя дело там совсем не в разных компиляторах, а в принципиально разных подходах к построению парсера.
А вот как насчёт сравнения объёма кода? Те два мои примера (обычный и реактивный) — это считай полностью законченный код. Т.е. туда добавляется обрамляющая функция main, добавляются инсклуды и всё, можно компилировать. Ты можешь показать такие же полноценные примеры реализующие в точности туже самую функциональность с использованием всех этих монадных библиотечек и т.п.? ) Оценим тогда удобство подходов для пользователя библиотечек...
Re[65]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Это было неофициальное обсуждение. Я думаю вряд ли в C++ добавят сахар для монад (возможно будет мощный препроцессор, на котором этот сахар легко реализуется (и не только он)).
Я к тому, что раньше подобные рассуждения C++'ников выглядели бы очень необычно. Сейчас это уже так, практически норма. )
А что за препроцессор? Что-то не слышал планов про изменения в этой области...
EP>Да, вот так: EP>
О, совсем классно. Явно надо ставить 1.55. Правда тут есть разница с моим вариантом. Точнее с моим вариантом, показанным на форуме. В реальности это был мой второй вариант, а первый работал технически в точности как и код выше. Т.е. ел char'ы и делал переключения контекста на каждый следующий символ. Мне это показалось как-то совсем не оптимально и я написал второй вариант, который ест string'и и делает переключения контекста только при новой порции данных, а не на каждый символ.
Re[63]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
S>А можно показать исходник этого "некоего класса rstring"?
Да без проблем. Только вот наверное уже нет особого смысла, т.к. это я использовал устаревшую (1.53) версию Boost'a, а в последней (1.55) версии уже прямо это всё готовое есть. Вот здесь http://www.rsdn.ru/forum/philosophy/5425281
Здравствуйте, Sinclair, Вы писали:
S>Нам нужны не "именно такие" данные, а возможность заставить XPath Expression работать по неполным данным, а также не держать в памяти весь буфер. S>Один пример — выражение типа //c[@value>1] будет мне возвращать нужные мне теги по мере поступления, а не после /b. Что может быть крайне ценным при, скажем, обработке XSLT. S>Другой пример — если я считаю некое выражение, скажем sum(//c[@class="red"]/value). Мне для этого подсчёта, вообще говоря, нахрен не упал весь документ одновременно. Отпарсили — прибавили — выкинули. Нужно понимать, что если я тяну документ по сети, и отдаю результат тоже в сеть, то большую часть времени мой "поток" спит. Когда я уже избавился от нативных потоков, и эта сплюшка не отъедает 1мб стека от адресного пространства моего процесса во время своего сна, то боттлнеком становятся вот такие "запоздало-реактивные" реализации. "Накапливаемый" документ отъедает непозволительно большую память на непозволительно большое время. В приведённом примере мне нужно, грубо говоря, хранить три строчки "состояния" парсера, а всё остальное выкидывать сразу после прочтения.
Ааа, понял о чём речь, как раз в стиле Спирита задачки. ))) Согласен, полезная и вполне реализуемая вещь. Причём реализуемая тривиально прямо сейчас, если отказаться от идеи написания полноценного xml парсера, а реализовать решения конкретных задачек (типа двух примеров выше). Т.к. для конкретных задач подойдут простейшие методы работы с текстом, а не полноценный разбор в AST. Ну а если требуется универсальное решение, то действительно надо писать свой реактивный xml парсер. Кстати, для упрощённого xml это делается на Спирите (который, как мы видели, может работать реактивно во всех смыслах) в несколько строк. Ну а для полноценного конечно же лучше писать с нуля.
Re[43]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
S>Я, наверное, чего-то не понял. Монада задаёт правила комбинирования функций. Поэтому из "одной концепции" получается весь зоопарк, включая M<R>(M<T>) и всякие F(G()).
Не любых функций. Монада задаётся (ну в основном, остальное мелочи) с помощью определения оператора >>= (например) вида "M<R> operator >>= (M<T>, M<R>(T));". Т.е. этот оператор позволяет функции вида M<R>(T) подействовать на M<T> и вернуть M<R>. И всё, точка. То, что путём некоторых манипуляций (тот самый лифтинг) мы можем из этого ещё и суметь подействовать на M<T> функцией вида R(T) и получить M<R>, означает всего лишь констатацию того факта, что любая монада является ещё и функтором. Однако подобное далеко не везде и например в обратную сторону это не верно. Т.е. если мы определим например некоторую сущность F через оператор вида "F<R> operator >>= (F<T>, R(T));", т.е. позволяющему действовать функции R(T) на F<T> и получать F<R> (кстати, именно такой сценарий больше всего наблюдался тут в агитации полезности монад), то из этого у нас уже не получится научиться действовать на F<T> функциями вида F<R>(T), так что монадой это не будет. Но применять функции R(T) к F<T> мы будем спокойно, причём без всякого лифтинга.
Абзац выше — это были как раз классические функциональные игры, хотя и записанные на C++. А вот тот мой пример с Apply — это наоборот был тупо императивный подход. Но при этом он работает не только с функторами или монадами, но и с чем угодно. Хоть с голыми значениями или коллекциями stl. А в сочетание с шаблонной магией C++ это ещё и позволяет писать сложный обобщённый алгоримы, работающий без исправления кода для всех этих разных сущностей одновременно. Причём без капли накладных расходов.
S>Вот, скажем, для Nullable<T> есть встроенные в язык C# правила для лифтинга операторов. А для пользовательских функций "правил лифтинга" нет. И нет никакой возможности описать "правила лифтинга" для своего типа Arbitrary<T> так, чтобы операторы и функции, определённые для T, автоматически конвертировались в операторы и функции, определённые для Arbitrary<T>. Нет средств выразить это в языке. И я никак не вижу способа добиться аналогичного результата в С++.
Я лично пока не понял, а в чём собственно проблема то?
S> Допустим, Петя определил тип optional<T> очевидным образом, предполагая использовать его для S>value-типов вроде int, double, и так далее. S>Как мне сделать так, чтобы этот тип корректно работал со всеми, в том числе ещё не написанными, типами? S>Вот Вася определил тип big_integer. S>Как мне писать программу с использованием optional<big_integer>? S>Для big_integer Вася определил множество операторов и функций. S>Как Пете описать тип optional<T>, чтобы я, прикладной программист, не должен был писать каждый раз S>
S>optional<big_integer> e = a.has_value() ? exp(a.value()) : optional<big_integer>::null;
S>
Ну так а чем функция типа Apply не подходит то? Что-то типа
template<typename R, typename T> auto Apply(optional<T> t, R (*f)(T)) {return t?f(*t):optional<R>();}
Пояснение: в boost'е у optional переопределён оператор bool (возвращает has_value()) и * (возвращает value()) — в таком варианте код записывается короче, но разницы в смысле нет.
Re[44]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>И да и нет. Смотря о чём говорить. Если говорить о смысловой части (разбиение задачи на функции, классы, алгоритмы вызовов и т.п.), то оно будет одинаковым на любом языке в исполнение одного программиста.
Очевидно — нет. Язык очень сильно определяет это разбиение.
>А вот количество букв для записи этих классов и т.п. естественно разное. И в C++ оно безусловно больше, чем в обычных языках с динамической типизацией. Но этот объём является ценой за то, что значительная часть ошибок отсеивается ещё на этапе компиляции. Или даже вообще не появляется, благодаря возможностям современных IDE для подобных языков.
Это большей частью сказки. Если сравнить С++ с гипотетическим С++, в котором все возможности ровно такие же, но нет типизации, то конечно нынешний зарулит гипотетический в минуса.
Реальность примерно такая — на динамических языках как правило надо кода гнать в разы меньше, а иногда даже на порядки. Собтсвенно смотри сам — спирит сливает даже самопалу на JS.
Re[66]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, Ikemefula, Вы писали:
I>>Нет, не одинаково. В спирите как видно, раза в три длинее и мутная нотация, мне например, было крайне сложно понять, что к чему
_>Ну давай запишем в одинаковом форматирование и без лишнего обрамление (ты бы ещё инклуды учёл), а только саму грамматику по честному.
Записывать нужно так, что бы было легко прочесть, а не что бы строчек было меньше. Эдак окажется что все программы на всех языках будут в одну строку.
_>БНФ: _>
Ты еще в одну строку все загони. В том виде как ты показал, грамматика нечитаемая.
_>Дело как раз не в экономии, а в другом. Хотя и с производительностью есть нюанс, т.к. при варианте Спирита можно вообще не выделять никакой памяти в процессе парсинга.
Это открытие претендует на нобелевку
>Тут дело в том, что как раз при таком варианте происходит реально реактивный парсинг. Т.е. результаты выдаются сразу после прихода данных. Например, мы можем задать какой-то сложный шаблон и при этом получать результаты при поступление только части данных для него.
Шо, в самом деле ? А я то думал это в любом реактивном парсере можно
I>>Дизайн в библиотеке я реализовал как мне было удобнее. В конечном итоге парсер может возвратить дерево, если его комбинаторы смапить на AST
_>Да не надо мне дерево. Ты мне покажи как мне написать функцию, которая будет хотя бы просто печатать разобранные данные.
Я же давал ссылку — там именно такая функция и приведена. Что еще надо, если та не подходит ?
>Вот для того же самого примера калькулятора пускай напечатает операторы другим цветом, а числа выведет в формате с плавающей точкой. А то у тебя пока все примеры были такие, что выдавали результат парсинга в виде некого одного предсказуемого результата (строки или числа там). А вот в каком виде мы получаем результат в случае если там некое выражение, состоящие из разных типов пока не понятно.
Будешь смеяться, я и это показл. Парсер возвращает пару значение-остаток. Для числа значением будет число, представь себе весь ужас. Для скобки будет скобка и тд и тд.
I>>аналого-цифровой преобразователь ?
_>Ну да, это основной источник внешней информации вида непрерывной последовательности отсчётов в робототехнике и вообще автоматике. ?
Ты наверное путаешь обработку сигналов и описание поведения и реакции на события. Первое оно полностью про ЦОС, включая АЦП и тд. А вот второе к этому никак не относится.
Если тебе надо управлять заслонкой котла в зависимости от показаний скажем десятка датчиков основываясь не только на мгновенных значениях датчиков, но и на предыстории, АЦП к этой задаче никакого отношения не имеет, ибо в задаче самая сложная часть это реакция системы.
>Соответственно, если твои тезисы верны, как раз работу с этим должны были первым делом перевести на всякие там реактивные монады и т.п.
С железом всё крайне консервативно. Типичный железячник пишет такие простыни императивного кода, которые никто, кроме него, прочесть не может. Единственное достоинство — оно как то работает.
FRP нужно для описания поведения и реакции на события некоторой системы. Раз в робототехнике, по твоим словам, это не надо, ну значит робототехника до этого еще не доросла.
вот два нормальных примера, которые еще раз внятно объясняют как это можно использовать
Здравствуйте, alex_public, Вы писали:
I>>Что касается сравнения производительности, то снова выходит сравнение нативного компилера с джытом. Это не интересно, для этого незачем городить парсеры.
_>Насчёт сравнения производительности согласен что не интересно. Хотя дело там совсем не в разных компиляторах, а в принципиально разных подходах к построению парсера.
Подход как раз именно один и тот же, это recursive descent. И я даже пудозреваю, что спирит это тоже реализация через комбинаторы парсеров, только в компайлтайм.
_>А вот как насчёт сравнения объёма кода? Те два мои примера (обычный и реактивный) — это считай полностью законченный код.
Нет там ничего законченого. Если сравнивать, то по всем аспектам, т.е. параллельно несколько ниток в потоке и нескольк потоков, шоб интереснее было.
>Т.е. туда добавляется обрамляющая функция main, добавляются инсклуды и всё, можно компилировать. Ты можешь показать такие же полноценные примеры реализующие в точности туже самую функциональность с использованием всех этих монадных библиотечек и т.п.? ) Оценим тогда удобство подходов для пользователя библиотечек...
У меня и так примеры полноценные, нужно только либу подключить
var lib = require('lib');
Всё По желанию можно неймспейс развернуть, это вот так делается
eval(lib.ns('lib'))
Можешь пробовать. Правда моей либы нет в публичном доступе, сложно будет позапускать.
Re[40]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Конечно работает. Если посмотрим на первый пример здесь на тему реактивных монадных парсеров (тот самый, что с заглавной страницы библиотечки Rxx), то я написал его ручной императивный аналог, работающий в реактивном режиме, меньше чем за минуту. Причём он оказался намного короче и намного проще для понимания.
Не проще В моем случае надо понять только грамматику. В твоем — дополнительно к этому приседания с состоянием.
Да и вообще, сколько смотрю, вот не могу понять, как твой вариант
>И вроде как ни у кого нет никаких претензий к его работе (придирки на счёт глобальной функции не принимаются — это тестовый пример и мы всегда можем одним движением заменить на объект, если надо завести много параллельных парсеров).
Это не придирки, это очень важный аспект, один из ключевых.
_>Но при этом я не раз повторял, что писать подобный код имеет смысл только в простых случаях (правда в этой темке что-то слишком часто попадались именно они). А в сложных следует использовать какие-то готовые библиотеки. Например конечные автоматы для каких-то событий или же Спирит, если говорим о тексте и т.п. Я надеюсь третьего (или уже четвёртого?) повтора этого моего тезиса будет достаточно, чтобы ты не "забыл" его снова? )
Во первых, ты изначально говорил про спирит и yacc, а выдал клочки императивного кода.
Во вторых, функционал имеет свойство развиваться. Отсюда ясно,что твои частные случаи особого смысла не имеют.
I>>Если ты таки решил прикрутить короутины, то всё в порядке. Правда теперь не ясно, почему ты сопротивлялся столько дней.
_>Оптимальное решение — это переписать Спирит под реактивный стиль. Но пока такого нет (а я точно не собираюсь делать подобное ради форумного спора ), то можно удовлетвориться и таким решением. Так сказать разумный компромисс между эффективностью кода и временем разработки.
Это абсолютно неважно — ты сказал что монады не нужны и сам же в конце концов предложил использовать монаду.
Твоя функция Analyze выдохнется через полтора дня, когда окажется, что требования поменялись и все надо будет переписать. А без короутины количество кода будет просто конским.
I>>Я ожидал что ты сразу переделаешь analyse на короутины, но мне уже надоело. Тебе теперь должно быть понятно, как императивно делается реактивный парсинг.
_>Короутины в Analyze? Зачем они там? Они могут понадобиться только если мы хотим заставить готовый библиотечный код (который мы не можем или не хотим менять) работать в асинхронном режиме. А если в Analyze только мой код, то больше ничего и не требуется.
I>>Любой реактивный парсинг сожрёт ресурсы и выдаст фильтрованый результат только по окончании. И это не важно, с++ или асп.нет или yesod(хаскель).
_>С чего бы это? Ты не видел примеры в этой темке? )
Re[44]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Ну так а чем функция типа Apply не подходит то? Что-то типа _>
_>template<typename R, typename T> auto Apply(optional<T> t, R (*f)(T)) {return t?f(*t):optional<R>();}
_>
_>Пояснение: в boost'е у optional переопределён оператор bool (возвращает has_value()) и * (возвращает value()) — в таком варианте код записывается короче, но разницы в смысле нет.
И? Дальше-то что? Ну вот написал Петя такую функцию Apply для своего optional. А мне-то как этим пользоваться?
Вот у меня был код на обычных big_integer:
Здравствуйте, Sinclair, Вы писали:
S>И? Дальше-то что? Ну вот написал Петя такую функцию Apply для своего optional. А мне-то как этим пользоваться? S>Я решил заменить их на option<big_integer>. Как теперь будет выглядеть мой код?
Пардон, что влезаю в разговор, но разве можно оставить существующий код без изменения при замене T на option<T>? По-моему, это очень опасно. Получается, придется брать какое-то значение по умолчанию и надеяться, что во всех местах оно подойдет.
Re[46]: Есть ли вещи, которые вы прницпиально не понимаете...
Дисклаймер: я только-только начинаю постигать смысл монад, поэтому могу ошибиться.
ARK>Пардон, что влезаю в разговор, но разве можно оставить существующий код без изменения при замене T на option<T>? По-моему, это очень опасно. Получается, придется брать какое-то значение по умолчанию и надеяться, что во всех местах оно подойдет.
В том-то и прелесть монад, что можно. В общем это зависит от самой монады, но конкретно с optional поступают так. Если есть операция F(t1,t2,...)над элементами типа T (или нескольких типов T1, T2, T3, ...), то та же самая операция над optional<T> возвращает null если хоть одно из значений null и F(t1,t2,...) все значения не null.
Причем, насколько я понимаю, это преобразование может сделать компилятор. Т.е. да, код выше не измениться.
Немного кода.
"Волшебный" метод bind, который из функции F<T> делает функцию F<optional<T>> (могу наврать с типами и все такое)
Тогда код Синклера никак не поменяется, но использоваться будет новая функция, созданная компилятором.
Супер! Я в восторге. Еще один из монадных принципов "щелкнул". AlexRK, спасибо, что своим вопросом заставил меня задуматься и понять что же хотел сказать Синклер.
СУВ, akava
Re[47]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, akava, Вы писали:
A>Дисклаймер: я только-только начинаю постигать смысл монад, поэтому могу ошибиться.
Я пока этой конструкцией не проникся. Option/Maybe, безусловно, полезен, а нафиг все остальное нужно, пока не понял.
A>В том-то и прелесть монад, что можно. В общем это зависит от самой монады, но конкретно с optional поступают так. Если есть операция F(t1,t2,...)над элементами типа T (или нескольких типов T1, T2, T3, ...), то та же самая операция над optional<T> возвращает null если хоть одно из значений null и F(t1,t2,...) все значения не null.
А если мне это не нужно? Может быть, мне нужно заменить отсутствующие значения на нули. А может в одном месте надо заменить на нули, а в другом — вернуть нулл, если хоть одно нулл.
А монада берет и лихо за меня принимает решения во всех местах. Причем я даже не вижу, в каких, все ведь компилится.
A>Супер! Я в восторге. Еще один из монадных принципов "щелкнул". A>AlexRK, спасибо, что своим вопросом заставил меня задуматься и понять что же хотел сказать Синклер.
Re[66]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
EP>>Это было неофициальное обсуждение. Я думаю вряд ли в C++ добавят сахар для монад (возможно будет мощный препроцессор, на котором этот сахар легко реализуется (и не только он)). _>Я к тому, что раньше подобные рассуждения C++'ников выглядели бы очень необычно. Сейчас это уже так, практически норма. )
Полиморфные лямбды уже есть, концепции-lite скоро будут. Надо двигаться дальше — модули, compile-time reflection, корутины, макросы, мульти-методы и т.д.
_>А что за препроцессор? Что-то не слышал планов про изменения в этой области...
(Точнее не препроцессор, а макросы). Это не изменения, а только разговоры — например. Главное не надо пытаться использовать макросы там, где достаточно лямбд, compile-time reflection, Boost.Fusion, etc.
Re[47]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, akava, Вы писали:
A>В том-то и прелесть монад, что можно. В общем это зависит от самой монады, но конкретно с optional поступают так. Если есть операция F(t1,t2,...)над элементами типа T (или нескольких типов T1, T2, T3, ...), то та же самая операция над optional<T> возвращает null если хоть одно из значений null и F(t1,t2,...) все значения не null. A>Причем, насколько я понимаю, это преобразование может сделать компилятор. Т.е. да, код выше не измениться.
[...] A>Тогда код Синклера никак не поменяется, но использоваться будет новая функция, созданная компилятором.
Только код поменяется, даже в Haskell. Не все функции можно автоматически лифтануть.
Например, было
a -> [a] -> a
а нужно
a -> [Maybe a] -> a
Придётся переписывать нарезая на continuations вручную, либо через do-сахар.
Точно также и в примере выше, можно переписать
array[i]*array[i]
в
get(array[i])*get(array[i])
тогда будет работать и с optional<int> и с int, и с int*, и с optional<int>*, и даже с future<int>.
Re[48]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, AlexRK, Вы писали:
ARK>А если мне это не нужно? Может быть, мне нужно заменить отсутствующие значения на нули. А может в одном месте надо заменить на нули, а в другом — вернуть нулл, если хоть одно нулл. ARK>А монада берет и лихо за меня принимает решения во всех местах. Причем я даже не вижу, в каких, все ведь компилится.
Монада будет принимать решения в тех местах, где это право ей делегируется явным вызовом bind.
То есть если код не был написан специальным образом — его придётся менять, bind'ы автоматом не расставятся.
Re[46]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, AlexRK, Вы писали:
ARK>Пардон, что влезаю в разговор, но разве можно оставить существующий код без изменения при замене T на option<T>? По-моему, это очень опасно. Получается, придется брать какое-то значение по умолчанию и надеяться, что во всех местах оно подойдет.
А почему нет? Просто нужно понимать, что как только у нас появилось undefined где-то в выражении, так сразу всё выражение стало undefined. А в тех немногих местах, где нужно оставить "значение по умолчанию", отличное он undefined, пишется ручной код.
Может, я не вижу какой-то опасности?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.