_>Я вообще то говорил не про некие отвлечённые, а про вполне конкретные примеры из поставки wxWidgets. Т.е. идём в папку wxWidgets (она у меня уже много лет на компьютере), видим там папку samples и в ней ещё 86 папок с примерами на все случаи. Аналогично с wxHaskell, только там насколько я помню примеров намного меньше. Находим одинаковые и сравниваем код. Я это сделал когда-то, когда смотрел на Haskell вообще и получил вполне однозначные выводы. Сейчас прямо за секунду (а иначе лень) легко повторить это не могу, т.к. уже давно стёр и wxHaskell и сам Haskell у себя с компьютера, как не нужное. Но если кто-то хочет проверить мои слова, то алгоритм очень простой. Причём я указал на эти самые примеры в первом же своём сообщение на эту тему.
Прекрати сочинять, ты потратил уже несколько часов на тему, как тебе лень тратить больше секунды на этот вопрос
Re[41]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Для того, что бы посчитать, сколько лишнего мусора в твоем коде. Кроме как сравнения с формализмом самой предметной области это сложно сделать.
Вообще то если поправить форматирование (БНФ у тебя в строчку, а в Спирите тоже самое на 4 раскидал) и не вставлять вещи типа namespace'ов (причём тут они к грамматике?), то выглядит практически одинаково.
I>AST нужен не просто так, а для конкретных вещей. Т.е. ты вводишь узел не там, где тебе надо кусочек грамматики описать, а там где тебе нужен этот узел
Можно и так. А можно и сразу.
I>Нет, разумеется. Какой смысл делать для символов?
Тогда я всё же не понял пока, тебе выдаётся дерево или что? Если дерево, то почему не целиком?
I>Я не знаю, что такое ADC
АЦП в смысле.
I>Тем хуже для тебя. Если не помогли ни примерЫ, ни ссылки на работы, то напрашивается один вариант — тебе нравится только императивный код и ничего другого ты видеть не хочешь.
Что-то ты совсем не понял меня. Ну я сейчас накидаю примерчик, так что поймёшь)
Re[35]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
K>Ну так надо делать не "шаблоны", а нормальный параметрический полиморфизм.
Чего чего? ) Это в смысле в рантайме?
K>Никакого реалистичного способа исправить "нетипизированность" шаблонов, разумеется, нет. Любое исправление просто сломает существующий код.
Речь шла не про плюсы, а про гипотетический язык с шаблонами, в котором компилятор их полностью проверяет.
K>Отличная шутка. Вообще-то норма, это когда проверяется именно исходники (бывает, что до рассахаривания), а потом уже генерируется код.
Правильно, исходник по которому генерируется код. И только он, а не некие абстрактные исходники. Для последнего существуют всяческие статические анализаторы. Хотя для обсуждаемой темы они естественно тоже не помогут.
K>Просьба прокомментировать заявление про ужасы IO была тоже очень давно. Сами сопоставить примеры вы отказываетесь, от объяснений уклоняетесь, указать что именно не нравится в моем примере вы не хотите.
Я не собираюсь повторяться в третий раз. Даже дважды уже было много. Тем более, что в ответ не услышал даже одного раза.
K>Понятно, что если мы перейдем к следующему пункту, то к этому уже не вернемся, на что вы, похоже, и рассчитываете. У меня же все основания настаивать на подробном обсуждении этого вопроса есть: преимущества контроля за эффектами будут выглядеть как оправдания каким-то невнятным ужасам, которые я вроде-бы автоматически признаю, переходя к следующему вопросу. Такой вариант меня не устраивает.
Здравствуйте, Sinclair, Вы писали:
S>Ну так вот эта функция apply — и есть монада. Точнее, чтобы она была монадой, надо к apply добавить функцию по конверсии "чистого" значения в "монадный" тип, и обеспечить выполнение монадических законов. S>Всё. Спор выглядит странным — это всё равно как говорить, что в С++ никакого ООП нет, а есть только данные и методы.
Ничего подобного. Это гораздо больше чем монада. И будет монадой только для определённого сочетания типа аргумента и функции. Т.е. могут быть варианты:
1. Функцию R(T) применяем к типу Т
2. Функцию M<R>(T) применяем к типу Т
3. Функцию R(M<T>) применяем к типу Т
4. Функцию M<R>(M<T>) применяем к типу Т
5. Функцию R(T) применяем к типу M<Т>
6. Функцию M<R>(T) применяем к типу M<Т>
7. Функцию R(M<T>) применяем к типу M<Т>
8. Функцию M<R>(M<T>) применяем к типу M<Т>
И всё это в одной концепции. Монадой же из всего этого будет только вариант 6. А будет ещё просто применение функций, фукторы и т.д. и т.п.
Re[61]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Когда спирит научится реактивный парсинг делать, тогда и сравним перформанс. А пока что сравнивать не с чем.
Собственно я тут подумал, а почему бы и нет! Ведь в той старой темке я тебе так подробно доказывал преимущества Boost.Coroutine на выдуманных тестовых примерах, а тут же на самом деле прямо идеальный реальный примерчик нарисовался. Причём он ещё и оптимальным по быстродействию может быть с учётом минимальных требований Спирита на входной итератор.
Значит смотри, вот пример обычной работы на Спирите:
string s="-333,-22,-1,0,1,22,333";
int sum=0, count=0;
parse(s.cbegin(), s.cend(), int_[([&](const int& n){cout<<n<<'\t'<<double(sum+=n)/++count<<endl;})]%',');
Теперь попробуем заставить Спирит работать реактивно. Для этого я написал (пара десятков строчек) некий класс rstring. Имея его, мы можем написать так:
Как видно, код самого парсера вообще не изменился. И естественно этот код выдаёт такой же результат как и первый вариант.
Ну что, покажешь аналоги этих примеров на базе тех монадных библиотечек? ) И тогда можем ещё и быстродействие сравнить (я поправлю примеры чтобы брали данные из файла и пришлю готовые бинарники), если рискнёшь конечно. )))
Re[42]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Как и у любого языка с динамической типизацией) Их нельзя использовать в серьёзном проекте, не обкладывая при этом тоннами тестов.
Это сказки. В динамическом языке прежде всего код пишется проще, его намного меньше.
Теоретически, это достижимо и в С++, но на практике дл интеграции нужно специальное АПИ. Высокоуровневые вещи, внезапно, это вагоны темплейтов, конские иерархии и сложные макры, через которые очень трудно продираться. А вот аналогичное АПИ на динамичеком языке предельно простое. Вот мне непонятно, как работает pipe, открыл да посмотрел — смешное количество строчек кода. А в С++ только перчисление только темплейтов и скобочек будет раз в десять больше.
Re[37]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Sinclair, Вы писали:
S>Этот код не работает. Как только в него приедет startSync и endSync по частям, ваш логгер облажается. S>Представьте на секунду, что Analyze вызывается на каждый приехавший символ.
Всё нормально там будет, строка накапливается в js. Т.е. если startSync и endSync приходят хоть по одному символу (но Analyze всё же вызывается построчно, т.е. в каждой строке по кусочку от startSync/endSync), то никаких проблем. Вот если весь Analyze вызывается для каждого символа (т.е. не по строкам, по которым там фильтрация осуществляется), то тогда действительно надо переделать.
S>В этом-то и сложность конверсии активного кода в реактивный — там, где активный код пишет "вынь да полож мне следующие 8 символов" (и встаёт в ожидании завершения IO, если у него исчерпался буфер), реактивный код должен уметь вернуть управление сразу же, как только отпроцессил входные данные.
S>Поэтому, например, корректных реактивных фильтров контента в ASP.NET приложениях в природе не встречается. Весь Analyze сводится к накапливанию аргументов в буфере, а сам анализ происходит по завершению.
Ну это проблемы исключительно ASP.NET. Как видно, достаточно универсальное решение делается на C++ всего в пару десятков строчек. )))
Re[62]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
I>>Когда спирит научится реактивный парсинг делать, тогда и сравним перформанс. А пока что сравнивать не с чем. _>Собственно я тут подумал, а почему бы и нет! Ведь в той старой темке я тебе так подробно доказывал преимущества Boost.Coroutine на выдуманных тестовых примерах, а тут же на самом деле прямо идеальный реальный примерчик нарисовался.
Я думал вы договорились Boost.Coroutine не использовать, поэтому и не упоминал
А так да — на корутинах можно легко приделать реактивный парсинг к Boost.Spirit.
Вообще, stackful coroutines можно использовать для упрощения использования многих монад, например optional<T>/expected<T>/future<T>/generator<T>(которая в свою очередь эмулирует корутины)/etc. Буквально недавно это обсуждалось в C++ ISO Proposals.
Но такая техника применима не ко всем монадам. Например это неприменимо к монаде list<T>, потому что одно и то же продолжение может вызываться несколько раз, а для stackful coroutine такой фокус не прокатит (стэк изменяется, объекты деструктятся и т.п.). Грубо говоря нужен полноценный call/cc.
Я думаю для stackless coroutines таких ограничений нет — объекты-автоматы можно копировать и вызывать несколько раз для одного checkpoint'а.
_>Для этого я написал (пара десятков строчек) некий класс rstring.
В новой версии Boost.Coroutine есть интерфейс input/output итераторов. По идее должно быть достаточно boost::coroutines::coroutine<char>::pull_type::iterator + boost::spirit::make_default_multi_pass.
Re[63]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Я думал вы договорились Boost.Coroutine не использовать, поэтому и не упоминал EP>А так да — на корутинах можно легко приделать реактивный парсинг к Boost.Spirit.
Хы, не, просто у меня есть привычка не заявлять непроверенный код. А проверять лень было, поэтому я про короутины и не упоминал. Но как видишь, народ меня всё же "добил" своими рассказами о великой сложности задачи реактивного парсинга, так что я ненадолго отбросил лень и набросал код. )))
EP>Вообще, stackful coroutines можно использовать для упрощения использования многих монад, например optional<T>/expected<T>/future<T>/generator<T>(которая в свою очередь эмулирует корутины)/etc. Буквально недавно это обсуждалось в C++ ISO Proposals.
О, это отдельная интересная тема для обсуждения... Вообще C++ в интересном направление стал сейчас развиваеться.
Правда она несколько противоположна тому моему примеру. Я же в нём как бы наоборот использую короутины, чтобы реализовать реактивный парсинг без всяких явных монад.
EP>Но такая техника применима не ко всем монадам. Например это неприменимо к монаде list<T>, потому что одно и то же продолжение может вызываться несколько раз, а для stackful coroutine такой фокус не прокатит (стэк изменяется, объекты деструктятся и т.п.). Грубо говоря нужен полноценный call/cc. EP>Я думаю для stackless coroutines таких ограничений нет — объекты-автоматы можно копировать и вызывать несколько раз для одного checkpoint'а.
Только их ещё конструировать надо самому. )
EP>В новой версии Boost.Coroutine есть интерфейс input/output итераторов. По идее должно быть достаточно boost::coroutines::coroutine<char>::pull_type::iterator + boost::spirit::make_default_multi_pass.
О, значит в новой версии может получится вообще автоматом? ) Позитивно. А у меня 1.53 стоит и я при выходе новых как-то не заметил, что короутины поправили. Надо будет посмотреть и возможно обновиться.
Но в любом случае даже свой код очень простой и короткий вышел (я использовал ещё boost::iterator_facade).
Re[64]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Вообще то если поправить форматирование (БНФ у тебя в строчку, а в Спирите тоже самое на 4 раскидал) и не вставлять вещи типа namespace'ов (причём тут они к грамматике?), то выглядит практически одинаково.
Нет, не одинаково. В спирите как видно, раза в три длинее и мутная нотация, мне например, было крайне сложно понять, что к чему
I>>AST нужен не просто так, а для конкретных вещей. Т.е. ты вводишь узел не там, где тебе надо кусочек грамматики описать, а там где тебе нужен этот узел
_>Можно и так. А можно и сразу.
Это экономия на спичках. Никакого профита это не
I>>Нет, разумеется. Какой смысл делать для символов?
_>Тогда я всё же не понял пока, тебе выдаётся дерево или что? Если дерево, то почему не целиком?
Дизайн в библиотеке я реализовал как мне было удобнее. В конечном итоге парсер может возвратить дерево, если его комбинаторы смапить на AST
I>>Я не знаю, что такое ADC
_>АЦП в смысле.
аналого-цифровой преобразователь ?
I>>Тем хуже для тебя. Если не помогли ни примерЫ, ни ссылки на работы, то напрашивается один вариант — тебе нравится только императивный код и ничего другого ты видеть не хочешь.
_>Что-то ты совсем не понял меня. Ну я сейчас накидаю примерчик, так что поймёшь)
Да-да, работай, а то кроме нерабочего Analyse от тебя было около одного нормального примера.
Re[64]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Хы, не, просто у меня есть привычка не заявлять непроверенный код. А проверять лень было, поэтому я про короутины и не упоминал. Но как видишь, народ меня всё же "добил" своими рассказами о великой сложности задачи реактивного парсинга, так что я ненадолго отбросил лень и набросал код. )))
Тебе про сложность никто ничего не говорил. Говорили про пользу и я просил пример для сравнения.
Re[38]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Всё нормально там будет, строка накапливается в js. Т.е. если startSync и endSync приходят хоть по одному символу (но Analyze всё же вызывается построчно, т.е. в каждой строке по кусочку от startSync/endSync), то никаких проблем. Вот если весь Analyze вызывается для каждого символа (т.е. не по строкам, по которым там фильтрация осуществляется), то тогда действительно надо переделать.
Надо переделывать, это ж очевидно, и я на это указываю уже хрен знает сколько раз.
S>>В этом-то и сложность конверсии активного кода в реактивный — там, где активный код пишет "вынь да полож мне следующие 8 символов" (и встаёт в ожидании завершения IO, если у него исчерпался буфер), реактивный код должен уметь вернуть управление сразу же, как только отпроцессил входные данные.
_>О да, дико сложно. ))) http://www.rsdn.ru/forum/philosophy/5424925
При чем здесь сложность ? Ты раз за разом выдавал неправильное императивное решение и настаивал что оно работает.
Если ты таки решил прикрутить короутины, то всё в порядке. Правда теперь не ясно, почему ты сопротивлялся столько дней.
Я ожидал что ты сразу переделаешь analyse на короутины, но мне уже надоело. Тебе теперь должно быть понятно, как императивно делается реактивный парсинг.
S>>Поэтому, например, корректных реактивных фильтров контента в ASP.NET приложениях в природе не встречается. Весь Analyze сводится к накапливанию аргументов в буфере, а сам анализ происходит по завершению.
_>Ну это проблемы исключительно ASP.NET. Как видно, достаточно универсальное решение делается на C++ всего в пару десятков строчек. )))
Ну сколько можно — еще одна попытка контекстно-свободный язык парсить регэкспом
Любой реактивный парсинг сожрёт ресурсы и выдаст фильтрованый результат только по окончании. И это не важно, с++ или асп.нет или yesod(хаскель).
Итого — не считая твоих попыток свести контекстно-свободную грамматику к регулярной принципиальной разницы не вижу.
Re[63]: Есть ли вещи, которые вы прницпиально не понимаете...
EP>Вообще, stackful coroutines можно использовать для упрощения использования многих монад, например optional<T>/expected<T>/future<T>/generator<T>(которая в свою очередь эмулирует корутины)/etc. Буквально недавно это обсуждалось в C++ ISO Proposals. EP>Но такая техника применима не ко всем монадам. Например это неприменимо к монаде list<T>, потому что одно и то же продолжение может вызываться несколько раз, а для stackful coroutine такой фокус не прокатит (стэк изменяется, объекты деструктятся и т.п.). Грубо говоря нужен полноценный call/cc. EP>Я думаю для stackless coroutines таких ограничений нет — объекты-автоматы можно копировать и вызывать несколько раз для одного checkpoint'а.
А я помню, как ты рассказывал что stackfull круче чем яйца, а оказыватся это совершенно разные механизмы и области применения у них всего лишь пересекаются
Re[62]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Теперь попробуем заставить Спирит работать реактивно. Для этого я написал (пара десятков строчек) некий класс rstring. Имея его, мы можем написать так: _>
_>Как видно, код самого парсера вообще не изменился. И естественно этот код выдаёт такой же результат как и первый вариант.
_>Ну что, покажешь аналоги этих примеров на базе тех монадных библиотечек? ) И тогда можем ещё и быстродействие сравнить (я поправлю примеры чтобы брали данные из файла и пришлю готовые бинарники), если рискнёшь конечно. )))
"Те" монандные библиотеки именно так и сделаны — через короутины. в js правда приходится туго, короутины толко в следующем стандарте, а сейчас эмуляция оных.
Что касается сравнения производительности, то снова выходит сравнение нативного компилера с джытом. Это не интересно, для этого незачем городить парсеры.
Re[64]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>А я помню, как ты рассказывал что stackfull круче чем яйца,
Так ведь действительно мощный механизм И кстати — корутины могут мигрировать из потока в поток (был когда-то вопрос на эту тему).
I>а оказыватся это совершенно разные механизмы и области применения у них всего лишь пересекаются
По сравнению с чем? С монадами? Ну да — но мы их и не сравнивали.
Если же ты про C# await — насколько я вижу, он тут ничем не поможет. У генерированных автоматов ведь нет value semantics?
Re[64]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
EP>>Я думал вы договорились Boost.Coroutine не использовать, поэтому и не упоминал EP>>А так да — на корутинах можно легко приделать реактивный парсинг к Boost.Spirit. _>Хы, не, просто у меня есть привычка не заявлять непроверенный код. А проверять лень было, поэтому я про короутины и не упоминал. Но как видишь, народ меня всё же "добил" своими рассказами о великой сложности задачи реактивного парсинга, так что я ненадолго отбросил лень и набросал код. )))
А, тогда понятно
_>О, это отдельная интересная тема для обсуждения... Вообще C++ в интересном направление стал сейчас развиваеться. _>Правда она несколько противоположна тому моему примеру. Я же в нём как бы наоборот использую короутины, чтобы реализовать реактивный парсинг без всяких явных монад.
Это было неофициальное обсуждение. Я думаю вряд ли в C++ добавят сахар для монад (возможно будет мощный препроцессор, на котором этот сахар легко реализуется (и не только он)).
EP>>Я думаю для stackless coroutines таких ограничений нет — объекты-автоматы можно копировать и вызывать несколько раз для одного checkpoint'а. _>Только их ещё конструировать надо самому. )
Я в общем про stackless. А конструировать их можно разными способами: вручную, макросами, внешним генератором и т.п.
EP>>В новой версии Boost.Coroutine есть интерфейс input/output итераторов. По идее должно быть достаточно boost::coroutines::coroutine<char>::pull_type::iterator + boost::spirit::make_default_multi_pass.
_>О, значит в новой версии может получится вообще автоматом? ) Позитивно.
Здравствуйте, alex_public, Вы писали:
_>Теперь попробуем заставить Спирит работать реактивно. Для этого я написал (пара десятков строчек) некий класс rstring.
А можно показать исходник этого "некоего класса rstring"?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[46]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали: _>Да, такое можно сделать, но тогда опять же непонятно зачем нам именно такие данные. Они же будут приходить в очень странной последовательности, по отношению к оригинальной структуре. Например для <a></a><b><c><c/></b> придёт как a, c, b. И зачем нам оно такое?
Нам нужны не "именно такие" данные, а возможность заставить XPath Expression работать по неполным данным, а также не держать в памяти весь буфер.
Один пример — выражение типа //c[@value>1] будет мне возвращать нужные мне теги по мере поступления, а не после /b. Что может быть крайне ценным при, скажем, обработке XSLT.
Другой пример — если я считаю некое выражение, скажем sum(//c[@class="red"]/value). Мне для этого подсчёта, вообще говоря, нахрен не упал весь документ одновременно. Отпарсили — прибавили — выкинули. Нужно понимать, что если я тяну документ по сети, и отдаю результат тоже в сеть, то большую часть времени мой "поток" спит. Когда я уже избавился от нативных потоков, и эта сплюшка не отъедает 1мб стека от адресного пространства моего процесса во время своего сна, то боттлнеком становятся вот такие "запоздало-реактивные" реализации. "Накапливаемый" документ отъедает непозволительно большую память на непозволительно большое время. В приведённом примере мне нужно, грубо говоря, хранить три строчки "состояния" парсера, а всё остальное выкидывать сразу после прочтения.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[42]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>И всё это в одной концепции. Монадой же из всего этого будет только вариант 6. А будет ещё просто применение функций, фукторы и т.д. и т.п.
Я, наверное, чего-то не понял. Монада задаёт правила комбинирования функций. Поэтому из "одной концепции" получается весь зоопарк, включая M<R>(M<T>) и всякие F(G()).
Вот, скажем, для Nullable<T> есть встроенные в язык C# правила для лифтинга операторов. А для пользовательских функций "правил лифтинга" нет. И нет никакой возможности описать "правила лифтинга" для своего типа Arbitrary<T> так, чтобы операторы и функции, определённые для T, автоматически конвертировались в операторы и функции, определённые для Arbitrary<T>. Нет средств выразить это в языке. И я никак не вижу способа добиться аналогичного результата в С++. Допустим, Петя определил тип optional<T> очевидным образом, предполагая использовать его для value-типов вроде int, double, и так далее.
Как мне сделать так, чтобы этот тип корректно работал со всеми, в том числе ещё не написанными, типами?
Вот Вася определил тип big_integer.
Как мне писать программу с использованием optional<big_integer>?
Для big_integer Вася определил множество операторов и функций.
Как Пете описать тип optional<T>, чтобы я, прикладной программист, не должен был писать каждый раз
optional<big_integer> e = a.has_value() ? exp(a.value()) : optional<big_integer>::null;
Уйдемте отсюда, Румата! У вас слишком богатые погреба.