Здравствуйте, alex_public, Вы писали:
_>Нюанс в том, что мутабельны (с вашей точки зрения)
Мне понравилось про мою точку зрения. Существует, мол, такая точка зрения у Клапауция, (очевидно ошибочная) что иммутабельные данные должны быть иммутабельными. Это, конечно, узкий взгляд на проблему, иммутабельные данные, конечно, мутабельны — а иначе обосновать поддержку в Ди иммутабельности не получится.
_>не сами контейнеры с данными, а итераторы по ним.
И сами контейнеры мутабельны. И итераторы по ним мутабельны.
_>Причём в Хаскеле всё буквательно то же самое, после оптимизации компилятором.
Ваш внутренний хаскель — это просто чудо какое-то.
_>А вот в D прямо в исходнике записаны циклы, а не рекурсии, так что не требуется дополнительная оптимизация
Высокий уровень! В лучших традициях фортрана.
_>Очевидно, что можно без проблем переписать все циклы на рекурсии
Правда программа будет падать — ну это не проблема, зато быстро падать будет.
_>но подобная работа будет верхом идиотизма.
И действительно. Оптимизация не нужна — можно же сразу оптимально написать, верно?
_>Не путайте просто иммутабельные структуры (которые в D легко делаются из мутабельных с помощью модификатора immutable)
Как из слона легко делается буйвол простой заменой надписи на клетке.
_>и оптимизированные персистентные структуры.
Так легко, что за это никто не берется — настоящие программисты легких путей не ищут!
_>Вот последнего в стандартное библиотеке действительно нет и именно это я и реализовал для нашего примера.
Но злые люди, по всей видимости, как только вы отвернулись на секунду, заменили эту реализацию на нечто малорелевантное и оно ушло на форум уже в таком виде.
_>Вы так и не поняли... ) Иммутабельный список в моём коде будет работать очень быстро, т.к. он просто не будет меняться. А если у него будет потребность меняться, то значит я уже не буду применять здесь иммутабельный список.
Чего уж тут непонятного? Очередное обоснование ненужности иммутабельности. Зачем список, который не меняется? Правильно, он бесполезен, а значит и иммутабельные структуры вообще.
_>Ну конечно, я сейчас будут плодить целую иерархию классов ради форумного примера.
В общем-то, если заявляется поддержка иммутабельности, то хотя-бы что-то такое уже должно быть, разве нет?
_>Достаточно уже того, что если вынести мой класс iarray в библиотеку, то клиентскому коду будут абсолютно недоступны никакие манипуляции с общим буффером — для клиентского кода операция ~ выглядит как нормальное создание нового массива.
Видимо "неклиентсвий" код никто не пишет. Погодите-ка. Вот вы же только что написали.
_>Код то есть,
Правда, к обсуждаемому вопросу никакого отношения не имеет, ну да ничего — можно ведь написать 256 постов в которых утверждать, что таки имеет. Потому, что иммутабельность — это мутабельность, свобода — это рабство и так далее.
_>но вы отделываетесь отговорками о неспособности его запустить из-за использования маргинального (для D) компилятора.
Как оказалось, компилятор, который генерирует более быстрый код в Ди маргинален — и правильно, жизнь медом казаться не должна.
_>Ну вообще то диапазоны D как раз очень эффективно оптимизируют именно это дело.
Оно и видно. (Ирония-иронией, но это действительно не самых плохой инструмент для построения конвейеров, к примеру дотнетный IEnumerable — значительно тормознее. Но и до хаскельных инструментов (у которых куча проблем и недоделок), как оказалось, не дотягивает)
_>Почему это не сработало в случае пары takeUntil.Any даже не представляю
Любые средства фьюжена имеют принципиальные ограничения и не справляются с устранением промежуточных данных для некоторых классов стадий конвейера. Они обычно классифицируются как хорошие/плохие продьюсеры/консьюмеры для данного метода фьюжена, это все разобрано в статьях и документировано. А для дишных рейнджей такие описания существуют?
_>но уверен что это какая-то недоработка реализации конкретно одного из этих алгоритмов, а не проблема самого принципа диапазонов.
Такие недоработки тоже бывают. К примеру, функция takeWhile из стандартной хаскельной библиотеки реализована так, что не участвует в фьюжене, не потому, что такой комбинатор невозможно реализовать правильным образом, а просто у авторов руки не дошли. Если его переписать, то стандартные функции работали бы так же быстро как те, что в примере с использованием stream-fusion, нагрузка на память была бы куда ниже и пришлось бы придумывать другой пример.
'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[103]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
K>И, следовательно, во вашей логике UML для C# использовать нельзя, правильно?
Вообще то тут речь о поддерживаемых языком парадигмах, а не подобном. А так то можно же спокойно написать на нормальном C++ (не на C) нормальную программу без единого класса. И соответственно описать её архитектуру с помощью UML нормально не получится. Но это же не значит что UML не применим в C++.
Т.е. у нас есть:
1. Языки с жёстко зашитой ООП парадигмой (типа Java/C#) — в них надо ещё очень постараться (например использовать только те самые статические классы) написать программу не подходящую под описание с помощью основной структурной UML диаграммы. Но при этом в любом случае там ещё будут полезны две основные диаграммы поведения.
2. Языки поддерживающие ООП парадигму (типа Питон/C++). Тут программа без ООП пишется легко и удобно, но эта возможность не часто используется для больших проектов. Так что в большинстве серьёзных случаев архитектуру удобно отображать в UML, ну а для мелочей в этом и так нет смысла. И опять же две важные диаграммы поведения очень полезны и без ООП.
3. Императивные языки не поддерживающие ООП (типа C или старого Фортрана). Архитектуру тут на UML вообще не нарисуешь. Но диаграммки поведения всё ещё весьма полезны.
4. Языки типа Хаскеля (и без ООП и с декларативностью/ленивостью). Тут ни архитектуру приложения на UML не нарисуешь (без придумывания виртуального ООП в языке), ни поведение не опишешь.
K>Если есть смысл объединить эти типы в одном модуле — то какой смысл различать, с какими типами каждая из этих функций "работает"? Обычно они будут "работать" с несколькими из этих типов.
Что значит с несколькими? ) Полиморфизм подразумевается?
K>Противопоставление тут непонятно. Вот, к примеру, возьмем инкапсуляцию в ООП. Когда вы изолируете особенности своей реализации от пользователя класса — вы ограничениями управляете, а пользователь наоборот "получает их свыше". Если вы сами пользователь какого-то класса — то "спускают свыше" уже вам. Так и тут.
Я могу не использовать этот класс, если он мне не нравится. )
K>Ничего не понимаю. Как вы можете "ограничить аргументы вашей функции" если кто-то захочет "работать с произвольными данными"? Может ли ваш "нормальный язык" создать камень, который не сможет поднять?
Ещё раз: в D мы можем потребовать принимать в качестве параметра только чистую функцию, а можем и не требовать этого (и соответственно принимать любые). В динамических языках и в языках типа Хаскеля такое не выйдет, правда по совершенно разным причинам.
K>Нет, это не терминологическая проблема. Есть, к примеру, мнение, что песок — неважная замена овсу. Проблема не в том, что нужно договориться и всем называть песок овсом. Проблема в том, что песок неважная замена для овса. Поэтому, если кто-то вас спрашивает: "Овес будет?" и вы ответите "Да", имея в виду, что будет песок — то потом окажется, что тот кто спрашивал — он именно овес и ожидал, а не песок. Если же все будут называть песок овсом, то проблема останется, потому что овес будут другим словом называть, например сепульками, и запрос на него останется, а вы сепульками будете называть опять песок. Ну или гравий, но от этого никому легче все равно не станет.
Не совсем так. Всё зависит от распространённости. К примеру если "истинный овёс" будет нужен только 0,01% специалистов, то вся индустрия спокойно будет использовать "овёсопесок" и все будут довольны, в том смысле что будут отлично понимать друг друга. А проблемы при этом будут только у явного меньшинства, которое возможно и захочет придумать свою новую терминологию (те же сепульки), но вряд ли она станет общепринятой.
K>Я вроде написал, почему я использовал не dmd (хотя я тогда думал, что ldc2 — это и есть dmd, только с llvm-бэкендом). Для того, чтоб исключить из сравнения фактор бэкенда. Судя по всему, решение было верным.
С чего бы это его исключать? ) Программисты то работают именно с реальными компиляторами.
K>Я поставил DMD32 D Compiler v2.065, правда уже на другой компьютер (3330), но все остальное (ghc, llvm, windows) осталось без изменений. K>Компилировал с -O -inline
А надо как минимум с "-O -release -inline", т.к. иначе куча всего лишнего выполняется. Причём даже прямо в том моём классе — на каждый вызов функции-члена iarray происходит тестирование инвариантов класса.
K>Поэтому ваши претензии к тому, что я сравнивал честнее — вообще звучат странно. Судя по find-версии, кодогенератор у dmd не фонтан, а единственное, что ldc2 не соптимизировал — это рекурсия. Впрочем, рекурсивно-лапшевая версия все равно тормозная, не понятно, за что вы боролись написав столько адского кода со строковыми параметрами шаблонов вместо нормальной передачи функций, кастами иммутабельных массивов в мутабельные и прочими ужасами.
У меня этот код работает быстрее всех остальных вариантов. Не знаю что вы там тестировали и как.
_>>Да, а что помешало протестировать мой бинарник? ) K>Очевидно, отсутствие вредной привычки запускать бинарники полученные непонятно от кого.
Уууу программист не имеющий ни одной виртуалки — это забавно в наше время. Похоже я когда-то давно угадал насчёт теоретиков и практиков. )
K>Задача — создавать промежуточные результаты в виде иммутабельных структур данных для проверки поддержки иммутабельности. Я знаю, что вы считаете, что для проверки иммутабельности нужно изменять массив на месте без всяких промежуточных данных вообще, но это кажется настолько неадекватным, что мне довольно трудно поверить, что вы все это серьезно пишете.
Для меня настолько же неадекватно выглядит идея связывать тестирование сборщика мусора, с поддержкой иммутабельности в языке.
K>Как мы выяснили ранее, хаскель ваших фантазий — язык крайне низкоуровневый и вообще убогий, так что ничего удивительного тут нет.
По сравнению с тем же Прологом он действительно таким ощущается. )))
K>Нет, отличие не в этом. Иммутабельная структура данных или нет видно из ее описания в библиотеке. K>К примеру, структуры данных которые участвуют в конкурсе со стороны хаскеля выглядят так: K>... K>Как видите, никаких массивов и изменений их на месте тут не просматривается. Понятно, что размещается это все в конечном итоге в некоем "мутабельном массиве" — памяти, но это не повод пропустить этот этап и заявлять, что мутабельные массивы — это и есть иммутабельные структуры данных.
Я правильно понял, что если я вытащу (сделаю скажем глобальной функцией или ещё что-то) из того моего класса оператор "~", то резко станет соответствовать вашим представлениям об иммутабельных структурах? )
Re[105]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
K>И сами контейнеры мутабельны. И итераторы по ним мутабельны.
Контейнеры не могут быть мутабельны, если мы используем модификатор immutable.
_>>Причём в Хаскеле всё буквательно то же самое, после оптимизации компилятором. K>Ваш внутренний хаскель — это просто чудо какое-то.
А в вашем внутреннем хаскеле не происходит оптимизация рекурсий в циклы? )
K>Высокий уровень! В лучших традициях фортрана.
Возможность записать явный цикл — это у нас теперь уже признак низкого уровня? ) А на мой взгляд цикл является более естественной вещью, чем рекурсия.
K>Чего уж тут непонятного? Очередное обоснование ненужности иммутабельности. Зачем список, который не меняется? Правильно, он бесполезен, а значит и иммутабельные структуры вообще.
Ну если вы не понимаете зачем в программе могут быть нужные неизменные данные, то мне тут даже и сказать нечего...
K>Любые средства фьюжена имеют принципиальные ограничения и не справляются с устранением промежуточных данных для некоторых классов стадий конвейера. Они обычно классифицируются как хорошие/плохие продьюсеры/консьюмеры для данного метода фьюжена, это все разобрано в статьях и документировано. А для дишных рейнджей такие описания существуют?
http://dlang.org/phobos/std_algorithm.html тут довольно подробно описаны все эти алгоритмы, включая оценки быстродействия и т.п. Но никаких упоминаний, почему сочетание until.any работает не оптимально, там нет. Видимо надо в исходниках смотреть, а мне как-то лень.
Кстати, замечу, что в реальном проекте (нашем тестовом на D), у нас кажется не встречается вообще ни одного конвейера. Не потому что не оптимально, а потому что просто нет таких задач.
Re[104]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Вообще то тут речь о поддерживаемых языком парадигмах
Возможность использования диаграммы классов с поддержкой парадигм связано слабо. Чтоб можно было UML использовать нужны 1) способы организации кода (функции/методы) 2) Способы группировки функций с функциями/данных с данными/данных с функциями 3) Нетривиальные отношения между получаемыми группами вроде наследования/вложения. Все. В большинстве языков все это есть, в том числе и в хаскеле.
_>Что значит с несколькими? ) Полиморфизм подразумевается?
Нет, подразумевается группа совместно используемых АлгТД, например АСТ какое-нибудь.
_>Я могу не использовать этот класс, если он мне не нравится. )
И в чем отличие от функции, которую вы тоже можете не использовать?
K>>Ничего не понимаю. Как вы можете "ограничить аргументы вашей функции" если кто-то захочет "работать с произвольными данными"? Может ли ваш "нормальный язык" создать камень, который не сможет поднять?
_>Ещё раз: в D мы можем потребовать принимать в качестве параметра только чистую функцию, а можем и не требовать этого (и соответственно принимать любые)
Но какая от этого практическая польза, если все что делает ваша "любая" функция, может делать и чистая?
_>Не совсем так. Всё зависит от распространённости. К примеру если "истинный овёс" будет нужен только 0,01% специалистов
В данном случае все эти рассуждения про одну сотую процента — не более чем попытка себя утешить принадлежностью к большинству. Вроде "у нас лямбд нету, зато нас легион!". На практике же соотношение часто бывает совсем иным. К примеру, программисты на практически всех языках с ГЦ ожидают от "лямбды" большего, чем может дать C++ под этой вывеской. И не смотря на то, что C++ действительно популярный язык — все эти программисты не на C++ составляют куда больше 0.01%.
_>и все будут довольны
Тут довольно трудно оценить сколько будут действительно довольны, а сколько просто уверять себя, что виноград — зелен.
_>С чего бы это его исключать? ) Программисты то работают именно с реальными компиляторами.
У вас отчетливо прослеживается тенденция сравнивать всякие плохо сравнимые вещи и оправдывать их какой-то "реальностью". Исключать надо вот почему: мы сравниваем производительность кода на двух языках, создаваемых на коленке горсткой энтузиастов. И если с одной стороны просить на весы бразиллион человекочасов вложенные в индустриального уровня бэкенд, то все противостояние этих двух кучек отойдет на второй план — это будет наколеночное ремесленничество против индустрии с понятным исходом.
Если дишный компилятор с LLVM бэкендом использовать нельзя или что вы там подразумевали под словом "нереален", то могу только посочувствовать.
_>А надо как минимум с "-O -release -inline"
А как оптимум? Эти ключи военная тайна какая-то? Почему бы вам с самого начала не дать всю информацию, необходимую для воспроизведения результата?
_>т.к. иначе куча всего лишнего выполняется.
Сюрприз: в хаскеле тоже проверяется "инвариант" и индексация массива. Там еще и буфер растущий по мере необходимости.
ОК, отключаем проверки, теперь хаскель версия с фиксированным буфером и без проверок
import Control.Applicative
import Control.Monad
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as M
import Control.Exception
data Buffer m a = Buffer { len :: !Int, buf :: !(M.MVector m a) }
over f (Buffer c b) = f . U.take c <$> U.unsafeFreeze b
(Buffer c b) <| a = Buffer (assert(M.length b > c)(c+1)) b <$ M.unsafeWrite b c a
buffer l n = flip (foldM (<|)) l . Buffer 0 =<< M.new n
main = print . len =<< flip (U.foldM step) (U.takeWhile (<= 2^24) . U.enumFromStepN (5::Int) 2 $ 2^24)
=<< buffer [2, 3::Int] (2^24) where
step primes x = do
t <- over (U.all ((/= 0) . (rem x)) . U.takeWhile ((<= x) . (^2))) primes
if t then primes <| x else pure primes
считает за 2.38, дишная find версия считает за 2.52, как хаскельная с изменяемым буфером с проверками и дишная рекурсивная версия за 2.6. Поскольку она все равно несколько медленнее find-версии, похоже, что какие-то секретные ключи вы все еще не выдали. Что там еще писать-то? -flamewar? -please?
Также нужно отметить, что производительность для хаскельного кода я измеряю диагностическими средствами рантайма (+RTS -s), а ди — утилитой time, т.к. более точного средства для ди не знаю. (впрочем, для хаскеля это дает расхождения где-то в 0.05сек, так что на практике, похоже, ничего не меняет).
_>Причём даже прямо в том моём классе — на каждый вызов функции-члена iarray происходит тестирование инвариантов класса.
Да, дишные инварианты в рекурсивном примере — это просто чудо какое-то. Ни хаскельная версия, ни find так здорово от отключения проверок не прибавили.
_>У меня этот код работает быстрее всех остальных вариантов. Не знаю что вы там тестировали и как.
Что я тестировал и как вы как раз знаете: я привожу полный код примеров, ключи компиляторов, версии компиляторов, ОС и модели процессоров. А вот я как раз не знаю, что вы тестировали и как. В этом и проблема. Мне и код нужно дописывать и ключи компилятора самому подбирать.
_>Уууу программист не имеющий ни одной виртуалки — это забавно в наше время.
Вы меня разоблачили. На самом деле я не программист, просто несколько лет назад начал претворяться программистом, чтоб поддержать разговор на rsdn. Так с тех пор и не могу остановиться.
Запускание вашего бинарника технически, конечно, осуществимо, только непонятно зачем его запускать. (В ожидании новых разоблачений основанных на том, что я не спешу анализировать бинарный код, а настоящий программист, конечно всегда предпочтет это чтению исходника и т.д.)
_>Похоже я когда-то давно угадал насчёт теоретиков и практиков. )
Насчет теоретиков — вы мне льстите.
_>Для меня настолько же неадекватно выглядит идея связывать тестирование сборщика мусора, с поддержкой иммутабельности в языке.
Это очевидно следует из того, что вы считаете, что "для проверки иммутабельности нужно изменять массив на месте без всяких промежуточных данных вообще". Было бы странно, если бы вы это восприняли иначе.
_>По сравнению с тем же Прологом он действительно таким ощущается. )))
У вас помимо воображаемого хаскеля еще и воображаемый пролог есть?
_>Я правильно понял, что если я вытащу (сделаю скажем глобальной функцией или ещё что-то) из того моего класса оператор "~",
Если вы сделаете это глобальной функцией ничего не изменится. Проблема в том, что для оценки производительности при работе с иммутабельными данными вы измеряете производительность при изменении массива на месте.
_>то резко станет соответствовать вашим представлениям об иммутабельных структурах? )
Не нужно делать что-то "соответствующее моим представлениям", используйте иммутабельную структуру. Отговорка про 0.01% тут не подойдет, потому что программистов, например, на JVM/CLR языках больше чем C++ программистов.
'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[106]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Контейнеры не могут быть мутабельны, если мы используем модификатор immutable.
Предположение о том, что запощенный от вашего имени код написали злые люди, похоже, подтвердилось. Потому, что в нем "иммутабельный" контейнер легко меняется:
auto d=cast(T*)(data);
d[size]=v;
return immutable IArray!(L, T)(data, size+1);
_>>>Причём в Хаскеле всё буквательно то же самое, после оптимизации компилятором. K>>Ваш внутренний хаскель — это просто чудо какое-то. _>А в вашем внутреннем хаскеле не происходит оптимизация рекурсий в циклы? )
Не знаю, о каком моем внутреннем хаскеле вы говорите, но в известных мне реально существующих имплементациях хаскеля после оптимизации кода на хаскеле получается не код на хаскеле, а на других языках/промежуточных представлениях.
_>Возможность записать явный цикл — это у нас теперь уже признак низкого уровня? )
Ну да. А по-вашему чего это признак?
_>А на мой взгляд цикл является более естественной вещью, чем рекурсия.
Видимо вы считаете, что высокоуровневость — это то, что вы считаете "более естественной вещью".
_>Ну если вы не понимаете зачем в программе могут быть нужные неизменные данные, то мне тут даже и сказать нечего...
Раньше я это знал, но вы рассеяли морок моего знания и сейчас я, как и вы, уже не знаю этого.
_>Кстати, замечу, что в реальном проекте (нашем тестовом на D), у нас кажется не встречается вообще ни одного конвейера.
Я это понял еще когда вы написали, что у вас не было проблем с фьюженом конвейеров в один цикл. Собственно, "у меня не было проблем с X" и следует понимать как "я не пользовался X" для любого X (но не наоборот!)
_>Не потому что не оптимально, а потому что просто нет таких задач.
Это точно, задачи "сделай мне все замечательно" бывают, а вот "сделай мне все замечательно с использованием конвейера" — нет. Правда задач "сделай мне все замечательно с использованием цикла" не бывает тоже, так что и циклы вы, наверное, не используете.
А инлайн-брейнфак в Ди есть?
'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[106]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
K>>Высокий уровень! В лучших традициях фортрана.
_>Возможность записать явный цикл — это у нас теперь уже признак низкого уровня? ) А на мой взгляд цикл является более естественной вещью, чем рекурсия.
Цикл некорректно сравнивать с рекурсией. Не ясно, что брать за критерий естественности. Если формулы из книжки по математики, то здесь, внезапно, рекурсия именно то и есть, типа f(x) = ля-ля-ля f(x-1)
А если брать в рассчет именно вычислитель, то именно здесь и появляется цыкл. Но фокус в том, что для тот же io фактически асинхронный вычислитель, и твоих классных циклов просто не будет, а будет нечто, которое выглядит, цветет и пахнет как та самая рекурсия.
_>Кстати, замечу, что в реальном проекте (нашем тестовом на D), у нас кажется не встречается вообще ни одного конвейера. Не потому что не оптимально, а потому что просто нет таких задач.
Пудозреваю, вы не знаете как им пользоваться.
Re[105]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
_>>Ещё раз: в D мы можем потребовать принимать в качестве параметра только чистую функцию, а можем и не требовать этого (и соответственно принимать любые)
K>Но какая от этого практическая польза, если все что делает ваша "любая" функция, может делать и чистая?
Не может. Выше уже было показано, как требовать strict purity, полностью аналогичную хаскелевой (с иммутабельными параметрами).
Re[105]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
K>Возможность использования диаграммы классов с поддержкой парадигм связано слабо. Чтоб можно было UML использовать нужны 1) способы организации кода (функции/методы) 2) Способы группировки функций с функциями/данных с данными/данных с функциями 3) Нетривиальные отношения между получаемыми группами вроде наследования/вложения. Все. В большинстве языков все это есть, в том числе и в хаскеле.
В теории всё верно. На практике же, если взглянуть на текущую версию стандарта UML, требуется поддержка именно ООП (для большей части структурных) и императивного кода (для большей части поведенческих).
K>Нет, подразумевается группа совместно используемых АлгТД, например АСТ какое-нибудь.
А будут ли вообще при таком раскладе функции специфичные для конкретного типа? Если нет, то тогда эти типы сводятся к аналогам обычных структур из C и соответственно говорить о какой-то инкапсуляции тут вообще странно.
_>>Я могу не использовать этот класс, если он мне не нравится. ) K>И в чем отличие от функции, которую вы тоже можете не использовать?
Вы не поняли, я не противопоставляю классы и функции. Речь о том, что в Хаскеле имеется большой ряд ограничений встроенных в сам язык (т.е. вне зависимости от каких-то библиотек и т.п.). В динамических языках у нас обычно нет даже возможности встроить подобные ограничения. А вот в языка типа D изначально практически никаких ограничений нет, но имеется набор инструментов для их создание, причём любой сложности (включая и все хаскелевские варианты).
K>Но какая от этого практическая польза, если все что делает ваша "любая" функция, может делать и чистая?
С чего бы это? )
K>В данном случае все эти рассуждения про одну сотую процента — не более чем попытка себя утешить принадлежностью к большинству. Вроде "у нас лямбд нету, зато нас легион!". На практике же соотношение часто бывает совсем иным. К примеру, программисты на практически всех языках с ГЦ ожидают от "лямбды" большего, чем может дать C++ под этой вывеской. И не смотря на то, что C++ действительно популярный язык — все эти программисты не на C++ составляют куда больше 0.01%.
Откуда это "в данном случае" возникли лямбды, если мы говорили про функцию map? )
Что касается лямбд, то это же всего навсего анонимный функтор — что там такого можно ожидать то? ))) Оно во всех языках одинаковое и отличается только набором обрамляющих знаков.
K>Тут довольно трудно оценить сколько будут действительно довольны, а сколько просто уверять себя, что виноград — зелен.
Я бы сказал, что большинство просто не в курсе при этом про какие-то другие варианты. )))
K>У вас отчетливо прослеживается тенденция сравнивать всякие плохо сравнимые вещи и оправдывать их какой-то "реальностью". Исключать надо вот почему: мы сравниваем производительность кода на двух языках, создаваемых на коленке горсткой энтузиастов. И если с одной стороны просить на весы бразиллион человекочасов вложенные в индустриального уровня бэкенд, то все противостояние этих двух кучек отойдет на второй план — это будет наколеночное ремесленничество против индустрии с понятным исходом. K>Если дишный компилятор с LLVM бэкендом использовать нельзя или что вы там подразумевали под словом "нереален", то могу только посочувствовать.
Если на D требуется достичь максимального быстродействия, то просто берётся gdc — думаю к его бекэнду никаких вопросов нет? ))) Но он по слухам (да и я натыкался помнится один раз) не очень стабилен под Windows, а мы занимаемся разработкой именно под ней, поэтому предпочитаем канонический dmd2. Он весьма стабильный под всеми платформами и в большинстве случаев не сильно проигрывает gdc.
K>А как оптимум? Эти ключи военная тайна какая-то? Почему бы вам с самого начала не дать всю информацию, необходимую для воспроизведения результата?
Достаточно написать в командной строке dmd и будут видны все опции (там нет такого беспредела с миллионом вариантов, как у gcc). Кстати там есть ещё много интересных, типа -unittest и т.п.
K>Сюрприз: в хаскеле тоже проверяется "инвариант" и индексация массива. Там еще и буфер растущий по мере необходимости.
Какой ещё инвариант проверяется в Хаскеле? ) Не стоит путать некоторые проверки нормального времени исполнения и по сути отладочный режим, который сыпит assert'ами. Вот как раз последнее и отключает -release. И как раз несколько таких assert'ов я для красоты и вставил в свой класс.
K>ОК, отключаем проверки, теперь хаскель версия с фиксированным буфером и без проверок K>...
Ох, жуть какая. ) Кстати, не принимайте подобные мои комментарии на свой счёт. Они относятся не непосредственно к вашему коду, а скорее к языку. Подозреваю, что ни один мастер не сможет написать на нём приемлемо выглядящий мутабельный код. )
K>считает за 2.38, дишная find версия считает за 2.52, как хаскельная с изменяемым буфером с проверками и дишная рекурсивная версия за 2.6. Поскольку она все равно несколько медленнее find-версии, похоже, что какие-то секретные ключи вы все еще не выдали. Что там еще писать-то? -flamewar? -please?
Да, странно. Остаются варианты: версия компилятора и способ измерения времени...
K>Также нужно отметить, что производительность для хаскельного кода я измеряю диагностическими средствами рантайма (+RTS -s), а ди — утилитой time, т.к. более точного средства для ди не знаю. (впрочем, для хаскеля это дает расхождения где-то в 0.05сек, так что на практике, похоже, ничего не меняет).
Я измерял так:
auto t=TickDuration.currSystemTick();
Primes!(2^^24)[].length.writeln;
writeln((TickDuration.currSystemTick()-t).msecs()/1000.0);
Предлагаю вам повторить измерения с таким способом. Думаю написать аналогичных код для Хаскеля не проблема? )
K>Да, дишные инварианты в рекурсивном примере — это просто чудо какое-то. Ни хаскельная версия, ни find так здорово от отключения проверок не прибавили.
Рекурсия тут ни при чём. Просто я для демонстрации приёмов контрактного программирования в D вставил в iarray несколько подобных проверок. Но надо не забывать, что эти проверки относятся по сути к отладочному режиму.
K>Что я тестировал и как вы как раз знаете: я привожу полный код примеров, ключи компиляторов, версии компиляторов, ОС и модели процессоров. А вот я как раз не знаю, что вы тестировали и как. В этом и проблема. Мне и код нужно дописывать и ключи компилятора самому подбирать.
Похоже проще мне снова поставить себе Хаскель и на этом закончить это бесконечное обсуждение с сомнительными измерениями. )))
K>У вас помимо воображаемого хаскеля еще и воображаемый пролог есть?
Не, Пролог у меня самый классический) Правда он встроен в приложение на C++ и умеет управлять микроконтроллерами в реальном времени.... )))
K>Если вы сделаете это глобальной функцией ничего не изменится. Проблема в том, что для оценки производительности при работе с иммутабельными данными вы измеряете производительность при изменении массива на месте.
Это оптимизация с помощью персистентной структуры. Вы же сами писали, что любите их. )
K>Не нужно делать что-то "соответствующее моим представлениям", используйте иммутабельную структуру. Отговорка про 0.01% тут не подойдет, потому что программистов, например, на JVM/CLR языках больше чем C++ программистов.
Вот уж кому говорить про иммутабельность, но не Java/С#. ))) Там у них даже с const напряги. )))
Re[107]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
K>Предположение о том, что запощенный от вашего имени код написали злые люди, похоже, подтвердилось. Потому, что в нем "иммутабельный" контейнер легко меняется: K>
А где здесь изменений контейнера? ) Я вижу только создание нового.
K>Видимо вы считаете, что высокоуровневость — это то, что вы считаете "более естественной вещью".
О, кстати довольно интересный вопрос... А что собственно вы называете высокоуровневостью? ) Уже предчувствую очередное интересное открытие... )))
Re[107]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Цикл некорректно сравнивать с рекурсией. Не ясно, что брать за критерий естественности. Если формулы из книжки по математики, то здесь, внезапно, рекурсия именно то и есть, типа f(x) = ля-ля-ля f(x-1) I>А если брать в рассчет именно вычислитель, то именно здесь и появляется цыкл. Но фокус в том, что для тот же io фактически асинхронный вычислитель, и твоих классных циклов просто не будет, а будет нечто, которое выглядит, цветет и пахнет как та самая рекурсия.
Не, это не похоже на рекурсию, потому как там есть изменяемое состояние. А у рекурсии (настоящей, а не оптимизированной в цикл) этого как раз нет — там всё в стеке.
I>Пудозреваю, вы не знаете как им пользоваться.
Ну расскажи как пользоваться конвейерами в коде вида:
Кстати про код. Этот кусочек кода в переводе на веб-технологии означает вещи примерно 15-18 летней давности — все делается по месту и явно обычным императивным подходом.
Из истории известно, что этот подход был сменен на разные mvc и более декларативный вид. Причины простые — если такого кода достаточно много, его адски тяжело майнтейнить. Поменять структуру реквестов, пакетов и тд и тд — почти невозможно.
Так что та самая революция о окторой ты говоришь, может и не наступить, потому что вы будете изобретать весь веб, только на С++.
Re[106]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, D. Mon, Вы писали:
K>>Но какая от этого практическая польза, если все что делает ваша "любая" функция, может делать и чистая? DM>Не может. Выше уже было показано, как требовать strict purity, полностью аналогичную хаскелевой (с иммутабельными параметрами).
Да нет, тут недопонимание. Я тут не про контроль чистоты в ди, с ним все понятно. Чистая функция в хаскеле делающая то же, что и "любая" в Ди.
Т.е. мой оппонент пишет:
Ещё раз: в D мы можем потребовать принимать в качестве параметра только чистую функцию, а можем и не требовать этого (и соответственно принимать любые). В динамических языках и в языках типа Хаскеля такое не выйдет, правда по совершенно разным причинам.
что в ди можно проверять чистая ли функция, а можно не проверять, а в хаскеле нельзя. Это верно, потому, что в хаскеле чистота функции не проверяется в смысле Ди — т.е. компайл-тайм рефлексией сигнатур и т.д., там функции чистые по построению. Но с практической точки зрения это не имеет значения, потому что за счет использования IO/ST чистые по построению функции могут делать то же самое что и те в Ди, для которых ничего не проверяется. Т.е. в mapM во время маппинга коллекции функция может делать все то же, что и в "непроверяющем" дишном map.
'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[109]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>У тебя последовательные вычисления с двумя if. Т.е. фактически, ты привел код y = f(x), что, очевидно, именно то что нужно для конвеера.
I>Твой код один к одному переписывается в монаду и вот у тебя практически тот самый конвейер.
Ну так покажи пример, как это стало бы выглядеть...
Re[106]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>В теории всё верно. На практике же, если взглянуть на текущую версию стандарта UML, требуется поддержка именно ООП (для большей части структурных) и императивного кода (для большей части поведенческих).
Как раз наоборот. В теории — что-то там может и требуется, но на практике это никого не волнует.
_>А будут ли вообще при таком раскладе функции специфичные для конкретного типа?
Если тот, кто строит диаграмму решит, что будут — значит будут.
_>Если нет, то тогда эти типы сводятся к аналогам обычных структур из C и соответственно говорить о какой-то инкапсуляции тут вообще странно.
Инкапсуляция на уровне модулей все равно.
_>Вы не поняли, я не противопоставляю классы и функции. Речь о том, что в Хаскеле имеется большой ряд ограничений встроенных в сам язык (т.е. вне зависимости от каких-то библиотек и т.п.).
Что за ограничения встроенне в язык?
_>А вот в языка типа D изначально практически никаких ограничений нет, но имеется набор инструментов для их создание, причём любой сложности (включая и все хаскелевские варианты).
Разница с хаскелем в том, что для него уже написана библиотека с контролем эффектов, построенная с использованием "инструментов для создания", а для Ди такой инфраструктуры пока нет, есть инструменты и ее "можно сделать".
_>С чего бы это? )
С того, что на хаскеле можно написать код с из чистых по построению функций, который будет делать то же, что и код на ди из "любых" функций без всякого контроля эффектов.
_>Откуда это "в данном случае" возникли лямбды, если мы говорили про функцию map? )
Возникли из моей фразы, на которую вы отвечаете:
Явление известное. Взять что-то, назвать "лямбда", "мап" или, еще лучше, "все_есть", а потом заявлять на форумах: "вот вы говорите, что у нас ничего нет, а у нас "все_есть" — вот она, эта функция".
иду прямо по списку. Популярность лямбды никак не повлияла на то, что под видом лямбды предлагается нечто недоделанное. Поэтому все рассуждения про то, что что-то там нужно 0.01% и в этом-то все дело не имеют никакого отношения к реальности: процент тех, кто видит разницу между овсом и песком никак не влияет на стремление предлагать песок тому, кому овес нужен.
_>Что касается лямбд, то это же всего навсего анонимный функтор — что там такого можно ожидать то? )))
Можно ожидать решенную UFP, например, а не путанные оправдания о том, что UFP одновременно и решена и ее решение не нужно.
_>Оно во всех языках одинаковое и отличается только набором обрамляющих знаков.
Почти во всех языках оно примерно одинаковое, с решенной UFP, а в плюсах — нет. В этом и дело.
_>Я бы сказал, что большинство просто не в курсе при этом про какие-то другие варианты. )))
Ну да. И если они вдруг что-то слаще моркови попробуют — у них и потребности соответствующие появятся.
_>Если на D требуется достичь максимального быстродействия, то просто берётся gdc — думаю к его бекэнду никаких вопросов нет? ))) Но он по слухам (да и я натыкался помнится один раз) не очень стабилен под Windows, а мы занимаемся разработкой именно под ней, поэтому предпочитаем канонический dmd2. Он весьма стабильный под всеми платформами и в большинстве случаев не сильно проигрывает gdc.
Замечательно. Есть ldc, но его использовать нельзя. Зато есть gdc, но его использовать нельзя. Зато есть dmd, который не сильно проигрывает (на самом деле заметно).
_>Достаточно написать в командной строке dmd и будут видны все опции (там нет такого беспредела с миллионом вариантов, как у gcc).
Конечно будут видны все. А не те, что вы использовали. Но нужно то знать те, которые вы использовали для замеров.
K>>Сюрприз: в хаскеле тоже проверяется "инвариант" и индексация массива. Там еще и буфер растущий по мере необходимости.
_>Какой ещё инвариант проверяется в Хаскеле? )
Да тот же самый, что позиция "курсора" не превышает длины буфера.
_>Ох, жуть какая. ) Кстати, не принимайте подобные мои комментарии на свой счёт. Они относятся не непосредственно к вашему коду, а скорее к языку. Подозреваю, что ни один мастер не сможет написать на нём приемлемо выглядящий мутабельный код. )
Конечно жуть. Ни циклов, ни рекурсивной лапши, ни даже передачи лямбд в текстовом виде! Просто кошмар какой-то.
_>Да, странно. Остаются варианты: версия компилятора и способ измерения времени...
Т.е. никаких больше ключей не было. К чему же было загадочное "как минимум"? Еще могу предположить, что вы взяли наихудший результат для find и наилучший для рекурсивной версии, а я беру средние.
_>Предлагаю вам повторить измерения с таким способом.
так рекурсивный вариант — 2.48, а с find — 2.42. Как я и предполагал — нет принципиальной разницы с результатами time.
_>Думаю написать аналогичных код для Хаскеля не проблема? )
Не вижу особого смысла. Я же говорю, там есть статистика от рантайма, в том числе и время работы.
_>Рекурсия тут ни при чём.
Да это понятно, я просто называю его рекурсивным чтоб было понятно о каком примере речь идет.
_>Просто я для демонстрации приёмов контрактного программирования в D вставил в iarray несколько подобных проверок.
Вы говорили что инвариант проверяется при каждом вызове метода класса. Т.е. не только при движении курсора, но и при каждом чтении элемента при обходе массива при поиске? Если так, то понятно, откуда тормоза. Миллион проверок при движении курсора погоды не сделают, а такие вот проверки — легко.
_>Похоже проще мне снова поставить себе Хаскель и на этом закончить это бесконечное обсуждение с сомнительными измерениями. )))
Ну, если вы будете выдавать сомнительные измерения — то обсуждение на этом не закончится. А в чем сомнительность моих измерений? Началось все с того, что вы заявили о разнице "в разы" (так и не понял, откуда вы ее взяли) при том, что конвейер у меня превращается в один цикл. И что это странно.
На что я ответил, что никакой разницы в разы нет и соответственно и странного ничего нет.
Что теперь-то вам кажется сомнительным?
_>Не, Пролог у меня самый классический) Правда он встроен в приложение на C++ и умеет управлять микроконтроллерами в реальном времени.... )))
Классический пролог — довольно слабый язык без типов и вообще интересных выразительных средств с необходимостью для чего-то нетривиального использовать хаки от красных катов до переписывания базы предикатов во время выполнения, что заставляет распрощаться с декларативной семантикой и думать в императивной, о том как решатель деревья обходит.
_>Это оптимизация с помощью персистентной структуры. Вы же сами писали, что любите их. )
1) Это ручная оптимизация, и следовательно не имеет отношения к поддержке иммутабельности языком.
2) Я ничего не говорил про "оптимизацию с помощью персистентной структуры", я говорил про оптимизации, которые позволяют персистентным структурам работать с приемлемой скоростью.
_>Вот уж кому говорить про иммутабельность, но не Java/С#. )))
В Java/С# иммутабельные данные применяются в более-менее заметных масштабах. Что возможно как раз потому, что нормальный ГЦ есть.
_>Там у них даже с const напряги. )))
Конечно. Но плюсовой const имеет весьма отдаленное отношение к иммутабельности, так что какие проблемы?
'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[108]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>А где здесь изменений контейнера? ) Я вижу только создание нового.
d[size]=v;
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[109]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Кстати про код. Этот кусочек кода в переводе на веб-технологии означает вещи примерно 15-18 летней давности — все делается по месту и явно обычным императивным подходом.
Что значит по месту? ) Это же серверный код.
Да, и это классическая архитектура большинства популярных веб-фреймворков. Типа там всяких Django, RoR, Node.js.
I>Так что та самая революция о окторой ты говоришь, может и не наступить, потому что вы будете изобретать весь веб, только на С++.
1. Не на C++, а на D. )
2. Изобретаем не мы — мы только пользуемся, и то в тестовом стажёрском проекте.
3. Модность их проекта в оригинальных компилируемых шаблонах, дающих одновременно и удобство и гарантии и огромное быстродействие (кстати все остальные компоненты заточены под это же, в том числе и на базе твоей любимой асинхронщины). А в реакции на ajax запросы там ничего инновационного нет, если конечно не считать того, что это происходит на статически типизированном нативном языке.
Re[110]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
I>>Кстати про код. Этот кусочек кода в переводе на веб-технологии означает вещи примерно 15-18 летней давности — все делается по месту и явно обычным императивным подходом.
_>Что значит по месту? ) Это же серверный код.
И что с того ?
_>Да, и это классическая архитектура большинства популярных веб-фреймворков. Типа там всяких Django, RoR, Node.js.
Нет, это не классическая архитектура. Это веб давности 15-18 лет, когда ты напрямую читаешь из реквеста и пишешь в респонс. то есть, тупо самый нижний уровень
Ты видишь здесь реквест, респонс ? Что характерно, издержек около нуля, в пересчете на затраты на диспетчеризацию из нижнего уровня в такой вот вид.
_>3. Модность их проекта в оригинальных компилируемых шаблонах, дающих одновременно и удобство и
Из твоего кода не видно, что вы используете шаблоны. Видно что вы изобретаете веб конца прошлого века.
>А в реакции на ajax запросы там ничего инновационного нет, если конечно не считать того, что это происходит на статически типизированном нативном языке.
Судя по коду, вся ваша инновация заключается в бинарном варианте json. Такой фокус есть в питоне, и в ноде и где угодно.
Re[110]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Ну так покажи пример, как это стало бы выглядеть...
В монадическом виде не нужны проверки на нулл. Отсюда надо
1. извлечь lang из req.form
2. извлечь юзера
3. послать все это функции для обновления состояния
4. сформировать результат
Здравствуйте, Klapaucius, Вы писали:
K>Как раз наоборот. В теории — что-то там может и требуется, но на практике это никого не волнует.
Ну да, потому что практики в общем то и нет. По вашему же признанию (что в мире хаскеле uml'ем практически не пользуются) кстати.
K>Что за ограничения встроенне в язык?
Мы же это уже пережёвывали в этой темке не раз. Надо повторять снова про невозможность написания кода для реальных приложений без уродливых монад? )
K>Разница с хаскелем в том, что для него уже написана библиотека с контролем эффектов, построенная с использованием "инструментов для создания", а для Ди такой инфраструктуры пока нет, есть инструменты и ее "можно сделать".
Совершенно верно. Правда для начала надо доказать полезность подобного. ) Этот вопрос тут уже обсуждался, но почему-то ответ пытался дать совершенно другой участник. А от вас так ни одного аргумента и не поступило.
K>С того, что на хаскеле можно написать код с из чистых по построению функций, который будет делать то же, что и код на ди из "любых" функций без всякого контроля эффектов.
Ага, в страшных монадах. ))) А можно и вообще обозвать (аналог магии каста в C++) любую внешнюя функцию чистой (даже если она при этом диски форматирует) и вообще никаких проблем. )))
K>Возникли из моей фразы, на которую вы отвечаете: K>
Явление известное. Взять что-то, назвать "лямбда", "мап" или, еще лучше, "все_есть", а потом заявлять на форумах: "вот вы говорите, что у нас ничего нет, а у нас "все_есть" — вот она, эта функция".
Это всё понятно. Но я не понял куда собственно делось обсуждение map'а? ) Или вам так неприятно узнавать, что в индустрии обычно подразумевают под этим понятием совсем не то, что вы? )
K>Можно ожидать решенную UFP, например, а не путанные оправдания о том, что UFP одновременно и решена и ее решение не нужно.
UFP относится к замыканиям, а не к лямбдам. Это конечно вещи связанные, но совсем не синонимы. Снова у вас какая-то путаница в терминологии.
_>>Какой ещё инвариант проверяется в Хаскеле? ) K>Да тот же самый, что позиция "курсора" не превышает длины буфера.
Выход за границы массива — это другое. И кстати в D тоже умеет проверяться автоматом (в срезах тех самых). А у меня был инвариант проверяющий, что грубо говоря не кончилась свободная память (пул в нашем случае). Ну и плюс ещё один, проверяющий что у нас вообще выделена память (дело в том, что D не позволяет запретить публичный конструктор для структур, в отличие от классов, т.е. там всегда доступен некий дефолтный init).
K>Т.е. никаких больше ключей не было. К чему же было загадочное "как минимум"? Еще могу предположить, что вы взяли наихудший результат для find и наилучший для рекурсивной версии, а я беру средние.
Ого. Т.е. вы даже не знаете самых элементарных принципов подобного тестирования? Что надо всегда проводить серию тестов и брать минимальное значение для каждого испытуемого.
Даже не знаю теперь о чём вообще говорить ещё при таком раскладе...
K>так рекурсивный вариант — 2.48, а с find — 2.42. Как я и предполагал — нет принципиальной разницы с результатами time.
У меня рекурсивный вариант стабильно быстрее. В среднем процентов на 8. Причём это так должно быть и с теоретической точки зрения, т.к. find генерирует промежуточную структуру в результате своей деятельности, а не индекс или самое значение (оно потом получается дополнительным вызовом к этой структуре).
Ваши же результаты весьма загадочны.
K>Не вижу особого смысла. Я же говорю, там есть статистика от рантайма, в том числе и время работы.
Для чистоты сравнения. Мало ли что там ваш рантайм показывает. )))
K>Ну, если вы будете выдавать сомнительные измерения — то обсуждение на этом не закончится. А в чем сомнительность моих измерений?
Они не совпадают ни с моими результатами (хотя бы в сравнение двух версий кода на D), ни с результатами D. Mon'а (у него было вменяемое сравнение кода на D с кодом на Хаскеле), на которые я и ориентировался при своих оценках.
K>Классический пролог — довольно слабый язык без типов и вообще интересных выразительных средств с необходимостью для чего-то нетривиального использовать хаки от красных катов до переписывания базы предикатов во время выполнения, что заставляет распрощаться с декларативной семантикой и думать в императивной, о том как решатель деревья обходит.
В любом случае он находится явно на более высоком уровне абстракции, чем Хаскель, т.к. у него хотя бы уже есть этот решатель. )))