Z3 theorem prover и вывод типов
От: VladD2 Российская Империя www.nemerle.org
Дата: 01.10.10 21:23
Оценка: 8 (1)
Думаю, что в этом форуме обитает народ занимающийся theorem prover-ами и прочими логическими штуковинми.

Вопрос к ним...

Можно ли использовать Z3 theorem prover (видео по его кострэйн-солверу) для вывода типов в языке вроде Nemerle?

Посню в чем собственно сложность в выводе типов.

Основных проблемы...
1. В системе типов имеется наследование (subtyping).
2. Присутсвтует явное и не явно (в том числе определяемое пользователем) приведение типов.
3. Присуствуют перегрузки.

Все в месте создает очень сложный граф поиска корректной программы в которой выбераются тип, перегрузки и приведения типов при которых программа может быть успешно типизирована.

Сейчас используется следующий алгоритм решения этой задачи.

При типизации выражений внутри отдельного метода строится граф типов. Для построения и проверки графа используется кострэйн-солвер написанный Москалем еще в бытность учебы в универе. Далее создаются объекты отложенной типизации которые могут содержать недотипизированные части АСТ и такие гипотетические ветви типизации применяющие разные вариант перегруженных методов. Далее начинается последовательные попытки стипизировать все возможные сочетания перегруженных методов, приведений типов и уточнений типов.

При каждой попытке констрэйн-солвер бэкапит состояние, уточняет граф типов и откатывает состояние к исходному. Если при такой попытке произошла ошибка вариант отбрасывается. Если ошибок не было и все типы вывелись, то ветвь принимается за успешную. Если ошибок не было, но есть неизвестные типы, производится попытка вычисления других ветвей в надежде на то, что эти вычисления уточнят некоторые типы в граффе и на следующей итерации позволят проверить недотипизированные ветви. Делается ряд итераций пока не выведутся все типы, или пока очередная итерация не перестанет изменять граф типов. Последнее свидетельствует об ошибке в программе. Первое об успехе.

Проблемами текущего алгоритма являются:
1. Серьезные тормоза вызванные огромным числом переборов и тем что постоянно требуется откатывать состояние солвера (при спекулятивных типизациях).
2. Сам процесс получается, скажем так, "императивным" в том смысле, что вместо построения некой модели всего кода метода и ее проверки мы вынуждены искать путь путем проб и ошибок. Попробовали стипизировать так — не получилось, попробовали иначе — получилось... поехали дальше... Фактически наличие вложенных вызовов и большого количества перегрузок (что характерно, скажем для линка) приводит к тому, что одно тело метода может типизироваться 1-3 секунды (на современном процессоре).
3. Иногда шаги типизации дублируются. Один и тот же алгоритм может выполняться при прямой типизации и при отложенной.

Хотелось бы как-то ускорить этот процесс и сделать его более структурированным.

В идеале хотелось бы построить что-то вроде модели кода метода содержащей в отдельных АСТ-ветках некое дерево возможных вариантов типизации (например, для разных перегрузок метода), скормить эту модель некому "думателю" и получить модель дерево в котором остались только корректные подветки.

Правильно ли я понимаю, что Z3 как раз то что нам нужно?
И возможно ли вообще (на современном этапе развития науки) то что я описал?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Z3 theorem prover и вывод типов
От: Sinix  
Дата: 02.10.10 05:19
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Можно ли использовать Z3 theorem prover (видео по его кострэйн-солверу) для вывода типов в языке вроде Nemerle?


Хз. Ссылки лежат где-то в конце стопки "разобраться".
Но судоку с его помощью решали, это точно
http://community.bartdesmet.net/blogs/bart/archive/2009/04/15/exploring-the-z3-theorem-prover-with-a-bit-of-linq.aspx
http://community.bartdesmet.net/blogs/bart/archive/2009/04/19/linq-to-z3-theorem-solving-on-steroids-part-0.aspx

Весь сериал (сейчас не открывается):
http://community.bartdesmet.net/blogs/bart/archive/tags/Z3/default.aspx
Re: Z3 theorem prover и вывод типов
От: vdimas Россия  
Дата: 03.10.10 13:57
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>...

VD>При каждой попытке констрэйн-солвер бэкапит состояние, уточняет граф типов и откатывает состояние к исходному. Если при такой попытке произошла ошибка вариант отбрасывается. Если ошибок не было и все типы вывелись, то ветвь принимается за успешную. Если ошибок не было, но есть неизвестные типы, производится попытка вычисления других ветвей в надежде на то, что эти вычисления уточнят некоторые типы в граффе и на следующей итерации позволят проверить недотипизированные ветви. Делается ряд итераций пока не выведутся все типы, или пока очередная итерация не перестанет изменять граф типов. Последнее свидетельствует об ошибке в программе. Первое об успехе.

Ну это же прямо описание работы машины Пролога. Уже ведь советовали использовать какой-нить готовый движок-вычислитель для этого раздела программирования — "программирование в ограничениях".

Да, именно, перебор, откат, вывод всех возможных цепочек. Входное АСТ надо представить этой машине в виде фактов, а правила выводов типов — в виде утверждений. Затем, если будет только одна цепочка вывода — всё ok, если ни одной — типы не вывели, если более одной, и при этом меняется семантика — неоднозначность.


VD>Проблемами текущего алгоритма являются:

VD>1. Серьезные тормоза вызванные огромным числом переборов и тем что постоянно требуется откатывать состояние солвера (при спекулятивных типизациях).

Это нормально. Программы на Прологе оптимизируются с помощью перестановки порядка правил (утверждений) таким образом, чтобы как можно раньше отбрасывать неудачные ветви рассуждений. Достигается банальным профайлингом и статистикой. Тем более, что у вас уже есть достаточно большие проекты на этом языке, т.е. расставить правила в таком порядке, чтобы некая "среднестатистическая" программа компилировалась быстрее всего, вполне возможно.


VD>2. Сам процесс получается, скажем так, "императивным" в том смысле, что вместо построения некой модели всего кода метода и ее проверки мы вынуждены искать путь путем проб и ошибок. Попробовали стипизировать так — не получилось, попробовали иначе — получилось... поехали дальше... Фактически наличие вложенных вызовов и большого количества перегрузок (что характерно, скажем для линка) приводит к тому, что одно тело метода может типизироваться 1-3 секунды (на современном процессоре).


Это что-то не так у вас. На старых машинах куда как более сложные вычисления быстрее происходили на Прологе. Надо взять заведомо хороший движок и не выдумывать велосипед. Движки пролога зачастую используют не написав строчки на самом Прологе, то бишь просто через некий АПИ некоей реализации заполняют данными (фактами + правилами) и запускают этот вычислитель. Есть подобные вычислители и вовсе далекие от собственно Пролога, представляющие из себя библиотеки по этой предметной области. Возможно, будет более удобно, чем некий движок Пролога (ибо у Пролога свои "тараканы"). Я подробно никогда не смотрел, пару раз пользовался движками именно от Пролога, но периодически подобные библиотеки/тулзы на глаза попадаются, когда роешь интернет по этой теме.

Вот буквально с первых строчек гугля:
http://mercurylanguage.ucoz.ru/

Mercury – это новый функционально-логический язык программирования со строгой типизацией, системой режимов аргументов и детерминизмом предикатов. Синтаксис частично унаследован от Пролога, система типов похожа на Haskell.
...
Так же, для ограничения перебора разработчикам Пролога пришлось включать в него некоторые недекларативные возможности, например, отсечения и циклы по неуспеху. В итоге при написании оптимизированной программы утрачивается вся красота декларативного подхода, и возникают серьезные трудности с пониманием кода и отладкой. Фактически Пролог не является чисто декларативным языком, он имеет императивные особенности, то есть многие части кода зависят от порядка следования целей в предикате.
...
Во-первых, нужно было что-то делать с типизацией, поэтому все современные логические языки содержат систему типов. Во-вторых, было бы очень неплохо перейти от интерпретации к компиляции программ, и наконец, надо решить главную проблему логического подхода – низкую производительность.

Разработчики Mercury сделали свой язык чисто декларативным, полностью убрав из него все императивные возможности. Ввели развитую систему типов, режимы аргументов, категории детерминизма предикатов и условные операторы. Оказалось, что такой подход имеет право на существование и, несмотря на некоторые трудности с организацией ввода-вывода, дает высокопроизводительный код. Причем оптимизация программы в этом случае производится автоматически самим компилятором, а не программистом, избавляя его от необходимости продумывания последовательности выполнения алгоритма.
...
Плюс, в Mercury разработчикам удалось объединить в одной парадигме логический и функциональный подход программирования. А так же бонусом является способность компилятора находить многие логические ошибки, за счет чего код программы становится более надежным.

Компилятор Mercury открыт и бесплатен (распространяется по лицензии GPL), работает на всех современных операционных системах. Особенностью языка является то, что он способен транслировать код во многие популярные языки программирования: C, Java, Erlang, IL для платформы .NET.


Т.е., вполне может быть, что такой движок, заточенный под правила Немерле, можно написать самому и оформить в виде библиотеки .Net.


VD>3. Иногда шаги типизации дублируются. Один и тот же алгоритм может выполняться при прямой типизации и при отложенной.


Ну, вообще, всё может зависеть от самих правил вывода, то бишь спецификации Языка в том виде, в которой вы решите её устаканить. Например, можно признать некую приоритетность применения правил, и, например, если "решение" найдено, то не запускать вычислитель для поиска других решений, т.е. для проверки на неоднозначность, чтобы сэкономить время. Или же, можно запускать такие проверки в других тредах.


VD>В идеале хотелось бы построить что-то вроде модели кода метода содержащей в отдельных АСТ-ветках некое дерево возможных вариантов типизации (например, для разных перегрузок метода), скормить эту модель некому "думателю" и получить модель дерево в котором остались только корректные подветки.


Абсолютно правильно. Надо имеющееся АСТ разложить на набор фактов. Или же, можно заведомо так АСТ спроектировать, чтобы оно сразу из себя структурированные для выбранной библиотеки/движка факты и представляло (это уже детали реализации, но может сильно ускорить в "обычных случаях", когда типизация не представляет трудности, и затраты на лишнюю трансформацию программы могут быть заметными в общем времени компиляции).


VD>Правильно ли я понимаю, что Z3 как раз то что нам нужно?


"Программирование в ограничениях" — единственно что нужно. Там много частных случаев и частных теорем, зачем же начинать с частностей? Возможно, тебя заинтересует потом большое кол-во частностей, которые покроют почти все моменты, что у вас возникают. Надо пройтись по этой теме с самого верха.


VD>И возможно ли вообще (на современном этапе развития науки) то что я описал?



Да этот кейз — классика программ, управляемых данными и ей тыщщи лет.
Re[2]: Z3 theorem prover и вывод типов
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.10.10 14:55
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Ну это же прямо описание работы машины Пролога. Уже ведь советовали использовать какой-нить готовый движок-вычислитель для этого раздела программирования — "программирование в ограничениях".


К сожалению — это даже сложнее чем пролог. Точнее несколько специализированее, так как есть подтипы.

Теорем-проверы и солверы — это и есть логическое программирование внедряемое в "гражданские" языки в виде библиотек.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Z3 theorem prover и вывод типов
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.10.10 21:10
Оценка:
Здравствуйте, vdimas, Вы писали:

VD>>Правильно ли я понимаю, что Z3 как раз то что нам нужно?


V>"Программирование в ограничениях" — единственно что нужно. Там много частных случаев и частных теорем, зачем же начинать с частностей? Возможно, тебя заинтересует потом большое кол-во частностей, которые покроют почти все моменты, что у вас возникают. Надо пройтись по этой теме с самого верха.


У них готове решение. Отлаженное и нацеленное на использование в виде библиотеки для наших целей.

Вопрос только в том, сможет ли эта штуковина работать в услвоиях системы типов поддерживающей наследование и т.п.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Z3 theorem prover и вывод типов
От: IT Россия linq2db.com
Дата: 04.10.10 21:17
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Это нормально. Программы на Прологе оптимизируются с помощью перестановки порядка правил (утверждений) таким образом, чтобы как можно раньше отбрасывать неудачные ветви рассуждений. Достигается банальным профайлингом и статистикой. Тем более, что у вас уже есть достаточно большие проекты на этом языке, т.е. расставить правила в таком порядке, чтобы некая "среднестатистическая" программа компилировалась быстрее всего, вполне возможно.


Или написать алгоритм, который будет сам тусовать правила в зависимости от частоты срабатывания/не срабатывания.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Z3 theorem prover и вывод типов
От: vdimas Россия  
Дата: 04.10.10 22:50
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>К сожалению — это даже сложнее чем пролог. Точнее несколько специализированее, так как есть подтипы.


VD>Теорем-проверы и солверы — это и есть логическое программирование внедряемое в "гражданские" языки в виде библиотек.


Во-первых, я тебе ссылку на типизированный вариант функционально-логического языка дал. Во-вторых, зачастую эти пруверы идут с серьезными ограничениями. Но, самое главное, что теорем пруверы обычно проверяют типы своей программы, на своем языке, а не твоем. То бишь пользы от них тебе будет разве что если исходники доступны, чтобы самому написать аналог.

Ну или в виде либы, как ты показал на z3. Я еще не смотрел, но если там есть движок поиска цели при заданных ограничениях — то это то, что тебе надо. Если нет — то только зря потеряешь время. На самом деле, для выводов типов нужен очень простой решатель. Сами правила могут быть сложны, это да, а решатель там банальный.

Меня только настораживает библиотечная реализация этого дела. Т.е. это ты заполнишь данными и вызовешь нечто вроде "реши!"... это же у тебя интерпретатор получится, прямо как в Прологе, а хотелось бы иметь компилированную и оптимизированную версию. То бишь факты пойдут как данные по-любому, а утверждения (самая нагрузочная часть) неплохо бы скомпилировать.
Re[3]: Z3 theorem prover и вывод типов
От: vdimas Россия  
Дата: 04.10.10 23:52
Оценка:
Здравствуйте, VladD2, Вы писали:

V>>"Программирование в ограничениях" — единственно что нужно. Там много частных случаев и частных теорем, зачем же начинать с частностей? Возможно, тебя заинтересует потом большое кол-во частностей, которые покроют почти все моменты, что у вас возникают. Надо пройтись по этой теме с самого верха.


VD>У них готове решение. Отлаженное и нацеленное на использование в виде библиотеки для наших целей.


Вот, смотрю доку. Улыбнуло 2 вещи: это native исполнение, и использование Doxygen для билда доки. Вот такая MS.
И тебе придется всю свою систему типов переводить на их Z3. Боюсь, могут быть проблемы с производительностью, при таком подходе.


VD>Вопрос только в том, сможет ли эта штуковина работать в услвоиях системы типов поддерживающей наследование и т.п.


Да какая разница, какая система типов? Главное, чтобы была непротиворечивой. Что именно волнует в наследовании? Неявное преобразование к базе? Ну дык и задать это отношение в виде наличия автоопределенного оператора приведения типа от наследника к базе.

В общем, я оказался прав здесь: http://www.rsdn.ru/forum/philosophy/3983774.aspx
Автор: VladD2
Дата: 04.10.10

У тебя обычный интерпретатор получится, если ты его задействуешь. Единственно что, парсить будет не интерпретатор, а ты, и будешь подавать готовое АСТ. И подавать запаришься, насколько я понял.

И что самое ужасное — перебор-то будешь делать тоже ты, а z3 лишь сможешь использовать для запуска проверки корректности текущей подстановки перебора... Не фонтан, в общем.

Надо смотреть в сторону компилируемых вещей. Даже компилирующий вариант Пролога может оказаться эффективнее для твоей задачи, чем z3, ибо правила у тебя будут "вшиты" компилятором в код, а не надо будет ими наполнять интерпретирующий солвер от z3.

Далее, у них заточка на зависимые типы идет. Например, на видео это хорошо видно, там проверяются арифметические ограничения/зависимости, которыми можно обогатить систему типов. Да и в примере тут: http://research.microsoft.com/en-us/um/redmond/projects/z3/test__managed_8cs_source.html метод prove_example2() показателен, можно на нем построить очень неплохой компилятор на зависимых типах, которому не обязательно будет явно давать динамические преобразования вроде такого:
var x = (CheckedInt<range>)usualInt;

Как предлагал Wolfhound, а вместо этого выводить/проверять тип x как подходящего CheckedInt<range> из контекста операций вокруг, например, учитывая некие вышестоящие проверки if(usualInt < range) и прочие ветвления.

Но в вашем случае есть не зависимая система типов, а обычная. И вы будете использовать такой минимум z3, который и самому на коленке не сложно, тем паче, что (повторюсь) перебирать варианты, то бишь осуществлять саму типизацию, вам надо будет самим. В общем, Z3 is not approved.

Кстати, интересный момент. Z3 можно будет использовать для оптимизации приватных типов, служащих для целей реализации тел методов генерируемой компилятором сборки. Можно будет попарно проверить все приватные типы, нагенеренные компилятором, и эквивалентные мн-ва типов "схлопывать" в один, как делает компилятор C++ от MS. Почему он и генерирует в 2-3 раза меньший бинарник, чем gcc. Потому что после "схлопывания" типов начинается "схлопывание" неожиданно ставших эквивалентными многих кусков кода. Правда, если типы уже представлены в виде дерева, то такую проверку вы можете выполнять и сами, ибо Z3 будет делать ровно аналогичное и вряд ли быстрее, учитывая время на перегон данных туда-сюда.
Re[3]: Z3 theorem prover и вывод типов
От: vdimas Россия  
Дата: 04.10.10 23:55
Оценка:
Здравствуйте, IT, Вы писали:

V>>Это нормально. Программы на Прологе оптимизируются с помощью перестановки порядка правил (утверждений) таким образом, чтобы как можно раньше отбрасывать неудачные ветви рассуждений. Достигается банальным профайлингом и статистикой. Тем более, что у вас уже есть достаточно большие проекты на этом языке, т.е. расставить правила в таком порядке, чтобы некая "среднестатистическая" программа компилировалась быстрее всего, вполне возможно.


IT>Или написать алгоритм, который будет сам тусовать правила в зависимости от частоты срабатывания/не срабатывания.


Ну, "первое приближение" в любом случае должно быть неплохим.

Еще можно как в шахматных программах: сделать базу из многих тысяч наиболее "типовых" ситуаций (опять же, из статистики), для которых ответ вычислен заранее. Как раз может помочь с убыстрением компиляции выражений LINQ.
Re[4]: Z3 theorem prover и вывод типов
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.10.10 11:56
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Во-первых, я тебе ссылку на типизированный вариант функционально-логического языка дал.


Зачем он мне в компилятере сдался? Если бы это была хотя бы нэйтив.
Мне нужен только солвер.

V>Во-вторых, зачастую эти пруверы идут с серьезными ограничениями.


А кому нужен универсальный продукт?

V>Но, самое главное, что теорем пруверы обычно проверяют типы своей программы, на своем языке, а не твоем.


Их язык меня не интересует. Мне нужен только АПИ который поволит построить модель и узнать ее характеристики. Это конечно тоже можно назвать языком, но это все же не язык в полном понимании этого слова.

V>То бишь пользы от них тебе будет разве что если исходники доступны, чтобы самому написать аналог.


Зачем мне аналог? Аналог у меня есть. Мне нужен качественный продукт обеспечивающий требуемые характеристики (в том числе высокую производительность).

V>Ну или в виде либы, как ты показал на z3.


Об этом и речь. Ни в каком другом виде мне ничего и не нужно. Причем библиотека должна быть управляемой (дотнетной). Или хотя бы иметь хороший интерфейс к дотнету.

V>Я еще не смотрел, но если там есть движок поиска цели при заданных ограничениях — то это то, что тебе надо.


Это там несомненно есть. Вопрос в том, потянет ли (причем с приемлемой производительностью) Z3 систему типов дотнета? Вот тут есть большие сомнения.

V>Если нет — то только зря потеряешь время. На самом деле, для выводов типов нужен очень простой решатель.


На самом деле — нет. От него требуется умение работать с системой типов дотнета (наследование, интерфейсы, приведение типов, перегрузка) и при этом работать очень быстро (справляться с очень большим деревом возможных решений).

V>Сами правила могут быть сложны, это да, а решатель там банальный.


Я уже говорил, что банальный есть. Но его производительность не устраивает.

V>Меня только настораживает библиотечная реализация этого дела. Т.е. это ты заполнишь данными и вызовешь нечто вроде "реши!"... это же у тебя интерпретатор получится, прямо как в Прологе, а хотелось бы иметь компилированную и оптимизированную версию. То бишь факты пойдут как данные по-любому, а утверждения (самая нагрузочная часть) неплохо бы скомпилировать.


Ну, хотелось бы многого. Но сгодится просто приемлемый по производительности и функционалу вариант.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Z3 theorem prover и вывод типов
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.10.10 12:13
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Вот, смотрю доку. Улыбнуло 2 вещи: это native исполнение, и использование Doxygen для билда доки. Вот такая MS.


Это исследователи.
У них есть дотнетная библиотека. Я пока не понял. Портирована ли она под дотнет целиком или является всего лишь оберткой над нэйтив реализацией.

V>И тебе придется всю свою систему типов переводить на их Z3. Боюсь, могут быть проблемы с производительностью, при таком подходе.


Ну, в некотором смысле — да, придется. Что до производительности, то меня эта штука и заинтересовала, так как они очень рекламируют ее скорость. Она же используется в коммерческих продуктах МС. Как минимум контракты верефицируются именно ею.

VD>>Вопрос только в том, сможет ли эта штуковина работать в услвоиях системы типов поддерживающей наследование и т.п.


V>Да какая разница, какая система типов? Главное, чтобы была непротиворечивой. Что именно волнует в наследовании? Неявное преобразование к базе? Ну дык и задать это отношение в виде наличия автоопределенного оператора приведения типа от наследника к базе.


Разница огромная. Модель очень сильно усложняется, что или требует специализированного решения, или очень высокой производительности.

Там среди прочего говорится о том, что квантор всеобщности поддерживается не полностью. А это может привести к проблемам с наследованием и интерфейсами.

V>В общем, я оказался прав здесь: http://www.rsdn.ru/forum/philosophy/3983774.aspx
Автор: VladD2
Дата: 04.10.10

V>У тебя обычный интерпретатор получится, если ты его задействуешь. Единственно что, парсить будет не интерпретатор, а ты, и будешь подавать готовое АСТ. И подавать запаришься, насколько я понял.

Оно и сейчас так. Сейчас есть класс Solver который строит граф типов. Так как строится он во время компиляции, а компиляция по сути — это рантайм для компилятора, то получается по любому интерпретация. С таким же успехом можно назвать интерпретацией любую ОО-модель.

V>И что самое ужасное — перебор-то будешь делать тоже ты, а z3 лишь сможешь использовать для запуска проверки корректности текущей подстановки перебора... Не фонтан, в общем.


Какой еще перебор? Логический вывод как раз должен делать Z3. Он и нужен чтобы уйти от тупых переборов.

V>Надо смотреть в сторону компилируемых вещей. Даже компилирующий вариант Пролога может оказаться эффективнее для твоей задачи, чем z3, ибо правила у тебя будут "вшиты" компилятором в код, а не надо будет ими наполнять интерпретирующий солвер от z3.


Правила там очень примитивные. Там сложная модель. А он по любому динамически набиваться будет.

V>Далее, у них заточка на зависимые типы идет.


Да ну? Откуда дровишки?
У МС вообще-то ни одного языка с зависимыми типами пока нет.
Это ты наверно о поддержке контрактов ведешь речь.

V> Например, на видео это хорошо видно, там проверяются арифметические ограничения/зависимости, которыми можно обогатить систему типов.


Можно обогащать, а можно тупо проверять констрэйны в коде. Что они и делают. При этом система типов о этих констрэйнах ничего не знает.

V>Да и в примере тут: http://research.microsoft.com/en-us/um/redmond/projects/z3/test__managed_8cs_source.html метод prove_example2() показателен, можно на нем построить очень неплохой компилятор на зависимых типах,


Наверно можно. Это уж точно минусом не будет. Сейчас вопрос все же о том подойдет ли нам этот Z3 или нет.

V>Но в вашем случае есть не зависимая система типов, а обычная. И вы будете использовать такой минимум z3, который и самому на коленке не сложно, тем паче, что (повторюсь) перебирать варианты, то бишь осуществлять саму типизацию, вам надо будет самим. В общем, Z3 is not approved.


Тебя постоянно тянет что-то там на коленке клепать. Зачем? Я как раз хочу уйти от наколеночных поделок в сторону промышленных стандартов.

V>Кстати, интересный момент. Z3 можно будет использовать для оптимизации приватных типов, служащих для целей реализации тел методов генерируемой компилятором сборки. Можно будет попарно проверить все приватные типы, нагенеренные компилятором, и эквивалентные мн-ва типов "схлопывать" в один, как делает компилятор C++ от MS.


Может и можно. Только не нужно. В дотнете нет таких проблем как есть в С++ просто потому что дженерики являются реальным сущностями, а не абстракциями которые исчезают после компиляции.

И вообще, Z3 похоже ведь хитрая и с его помощью много чего можно... потенциально. Это несомненный плюс. Но сейчас стоит другие вопросы. Подойдет ли он нам? Упростит ли он код компилятора по сравнению с использованием самопального словрера (который является самым простым звеном Z3)?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Z3 theorem prover и вывод типов
От: vdimas Россия  
Дата: 05.10.10 13:40
Оценка:
Здравствуйте, VladD2, Вы писали:


VD>Это исследователи.

VD>У них есть дотнетная библиотека. Я пока не понял. Портирована ли она под дотнет целиком или является всего лишь оберткой над нэйтив реализацией.

Дотнетная обертка, почему я и сильно сомневаюсь насчет бенефитов, перегон данных все бенефиты сожрет.


VD>Разница огромная. Модель очень сильно усложняется, что или требует специализированного решения, или очень высокой производительности.


Усложняется лишь тем, что ты должен подать на модель все пары видимых случаев наследников.


V>>В общем, я оказался прав здесь: http://www.rsdn.ru/forum/philosophy/3983774.aspx
Автор: VladD2
Дата: 04.10.10

V>>У тебя обычный интерпретатор получится, если ты его задействуешь. Единственно что, парсить будет не интерпретатор, а ты, и будешь подавать готовое АСТ. И подавать запаришься, насколько я понял.

VD>Оно и сейчас так. Сейчас есть класс Solver который строит граф типов. Так как строится он во время компиляции, а компиляция по сути — это рантайм для компилятора, то получается по любому интерпретация. С таким же успехом можно назвать интерпретацией любую ОО-модель.


О, нет. Вот тебе простой пример (пусть на C#, если ты не против).

Пусть у нас есть class X1 и наследник X2. И мы проверяем пару конструкций:
X1 x1 = GetX2();  // GetX2 имеет тип X2
var x2 = GetX2();


Давай запишем на псевдо-прологе, что мы имеем:
Пусть операция присвоения называется у нас "set", операция приведения типов "convert", а для более короткой записи мы будем писать "term/type" (поэтому псевдо-пролог).
Итак, факты и утверждения:

convert(_/T, _/T). — любой тип T сам в себя конвертируется (1)
set(exp/T1, exp/T2) :- convert(_/T2, _/T1). — утверждение, что операция присвоения валидна, только если существует преобразование из типа T2 в T1. (2)
convert(_/X2, _/X1). — факт, что наследник X2 приводим к базе X1 (3)

теперь проверяем на корректность первую строчку программы:
set(x1/X1, _/X2) ? — выдаст true, т.е. решение существует, потому что (2).

типизируем второе выражение:
set(x1/?, _/X2) — выдаст несколько вариантов, самый короткий — это ?=X2, получили как (2)->(1). Еще валидный ответ ?=X1, получается как (2)->(3).

Если включить больший контекст, например использование x1 ниже, то возможно ответ будет всего один.

Так вот, внимание. В чем проблема интерпретируемого подхода? В том, что в интерпретаторе все свалено в одну кучу, что при большом объеме правил и фактов страшно тормозит само по себе, ибо выгребает из одной коллекции. Т.е. мало того, что все утверждения еще проверить надо, в процессе проверки постоянно надо лезть в эти общие словари за термами. А если все утверждения у нас в компиллируемом виде, да еще реализованы максимально эффективно для каждого утверждения, например, для проверки отношения {наследник, база} нам не надо будет перебирать все подобные пары в словаре фактов, а у нас уже есть "закешированная" ссылка на все базы у наследника, поэтому такая проверка выполнится на много порядков быстрее. И так далее в таком же духе.

Интерпретация — это исполнение данных, никогда это быстро не будет, ни на каком волшебном Z3. Надо по-максимуму исполнять код.

V>>И что самое ужасное — перебор-то будешь делать тоже ты, а z3 лишь сможешь использовать для запуска проверки корректности текущей подстановки перебора... Не фонтан, в общем.


VD>Какой еще перебор? Логический вывод как раз должен делать Z3. Он и нужен чтобы уйти от тупых переборов.


Не увидел там движка поиска цели, показывали только решатель, который проверяет модель на корректность.


VD>Да ну? Откуда дровишки?

VD>У МС вообще-то ни одного языка с зависимыми типами пока нет.

При чем тут языки от MS, и возможности этого Z3?


VD>Это ты наверно о поддержке контрактов ведешь речь.


Зависимые типы — это и есть одна из возможностей поддержки контрактов на данные на уровне системы типов.


V>>Кстати, интересный момент. Z3 можно будет использовать для оптимизации приватных типов, служащих для целей реализации тел методов генерируемой компилятором сборки. Можно будет попарно проверить все приватные типы, нагенеренные компилятором, и эквивалентные мн-ва типов "схлопывать" в один, как делает компилятор C++ от MS.


VD>Может и можно. Только не нужно. В дотнете нет таких проблем как есть в С++ просто потому что дженерики являются реальным сущностями, а не абстракциями которые исчезают после компиляции.


Я имел ввиду, что в функциональном языке должно быть очень много вспомогательных типов/объектов, нагенеренных "по месту". Поэтому объем бинарника может быть довольно большим.

VD>И вообще, Z3 похоже ведь хитрая и с его помощью много чего можно... потенциально. Это несомненный плюс. Но сейчас стоит другие вопросы. Подойдет ли он нам? Упростит ли он код компилятора по сравнению с использованием самопального словрера (который является самым простым звеном Z3)?


Вряд ли упростит. Там только трансформация всех ваших данных, представляющих компилируемую программу, в модель Z3 — это дополнительное огромное кол-во кода, который сам по себе сложен и будет требовать массу времени для отладки.
Re[6]: Z3 theorem prover и вывод типов
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.10.10 14:48
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Дотнетная обертка, почему я и сильно сомневаюсь насчет бенефитов, перегон данных все бенефиты сожрет.


Это хреново с точки зрения переносимости.

V>Усложняется лишь тем, что ты должен подать на модель все пары видимых случаев наследников.


Это очень наивный взгляд на проблему.

V>О, нет. Вот тебе простой пример (пусть на C#, если ты не против).


V>Пусть у нас есть class X1 и наследник X2. И мы проверяем пару конструкций:

V>
V>X1 x1 = GetX2();  // GetX2 имеет тип X2
V>var x2 = GetX2();
V>


V>Давай запишем на псевдо-прологе, что мы имеем:

V>Пусть операция присвоения называется у нас "set", операция приведения типов "convert", а для более короткой записи мы будем писать "term/type" (поэтому псевдо-пролог)...

Это опять же очень наивный взгляд на проблему.

В реальности все обстоит примерно так...

У нас есть переменные которые могут представлять тип. Это почти тоже самое, что переменные пролога, но они могут представлять тип в системе типов дотнета. А это значит:
1. Переменная может ограничивать тип сверху и/или снизу. Скажем некоторая переменная может быть снизу ограничена типом IEnumerable[B-], а сверху List[B]. При этом реальный объект, может, например, иметь тип IList[B-]. Под "-" тут понимается этот тип или его предок, то есть это может быть, например, IList[A], если A является базовым типом для B. При унификации все это должно отслеживаться.
2. Надо учитывать, что типы баывают сами по себе разные. Это могут быть прост классы или структуры, могут быть кортежи, а могут параметры типов (как воплощенные, так и нет). В последнем случае так же присутствуют констрэйны, которые так же влияют на унификацию. Более того кроме самой унификации еще требуется операция влияющая на верхнюю и/или нижнюю границу типа (в немерле это называется Require (т.е. тип должен являться по крайней мере заданным типом).

Приведения типо и перегрузки уже должны учитываться в моделе которая описывает зависимости между типами.
Так нужно ввести сущность "вызов" которая будет описывать вызов функции. Там должны быть ветви для учета перегрузки и т.п.

V>Интерпретация — это исполнение данных, никогда это быстро не будет, ни на каком волшебном Z3. Надо по-максимуму исполнять код.


То что компиляция при прочих равных быстрее интерпретации — это очевидно уважаемый капитан Очевидность.
Вот только не факт, что прочие равные.

V>Не увидел там движка поиска цели, показывали только решатель, который проверяет модель на корректность.


Плохо смотрел.

VD>>Да ну? Откуда дровишки?

VD>>У МС вообще-то ни одного языка с зависимыми типами пока нет.

V>При чем тут языки от MS, и возможности этого Z3?


При том что он делался не ради самого себя. Его сделали для вполне конкретных применений. На сегодня это статическая верификация контрактов и проверка покрытия кода тестами.

VD>>Это ты наверно о поддержке контрактов ведешь речь.


V>Зависимые типы — это и есть одна из возможностей поддержки контрактов на данные на уровне системы типов.


Зависимые типы — это система типов. А а контракты — это контракты. Они пашут поверх имеющейся системы типов. Так что ты путаешь причину и следствие. Да, фича для реализации ЗТ у них есть, но нет, они ее делали не для этого.

V>Я имел ввиду, что в функциональном языке должно быть очень много вспомогательных типов/объектов, нагенеренных "по месту". Поэтому объем бинарника может быть довольно большим.


С этим справляется NGEN. У него уже есть такая фича.

VD>>И вообще, Z3 похоже ведь хитрая и с его помощью много чего можно... потенциально. Это несомненный плюс. Но сейчас стоит другие вопросы. Подойдет ли он нам? Упростит ли он код компилятора по сравнению с использованием самопального словрера (который является самым простым звеном Z3)?


V>Вряд ли упростит. Там только трансформация всех ваших данных, представляющих компилируемую программу, в модель Z3 — это дополнительное огромное кол-во кода, который сам по себе сложен и будет требовать массу времени для отладки.


Мне нужны рассуждения подкрепленные чем-то на что можно положиться, а не просто мнение в стиле "не взлетит".
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Z3 theorem prover и вывод типов
От: vdimas Россия  
Дата: 05.10.10 17:52
Оценка:
Здравствуйте, VladD2, Вы писали:


V>>Давай запишем на псевдо-прологе, что мы имеем:

V>>Пусть операция присвоения называется у нас "set", операция приведения типов "convert", а для более короткой записи мы будем писать "term/type" (поэтому псевдо-пролог)...

VD>Это опять же очень наивный взгляд на проблему.


Почему наивный? Я тебе показал принцип, как оно работает. Чтобы было видно "сваливание всего в кучу" при использовании интерпретации.

VD>В реальности все обстоит примерно так...


VD>У нас есть переменные которые могут представлять тип. Это почти тоже самое, что переменные пролога, но они могут представлять тип в системе типов дотнета. А это значит:

VD>1. Переменная может ограничивать тип сверху и/или снизу. Скажем некоторая переменная может быть снизу ограничена типом IEnumerable[B-], а сверху List[B]. При этом реальный объект, может, например, иметь тип IList[B-]. Под "-" тут понимается этот тип или его предок, то есть это может быть, например, IList[A], если A является базовым типом для B. При унификации все это должно отслеживаться.

VD>2. Надо учитывать, что типы баывают сами по себе разные. Это могут быть прост классы или структуры, могут быть кортежи, а могут параметры типов (как воплощенные, так и нет). В последнем случае так же присутствуют констрэйны, которые так же влияют на унификацию. Более того кроме самой унификации еще требуется операция влияющая на верхнюю и/или нижнюю границу типа (в немерле это называется Require (т.е. тип должен являться по крайней мере заданным типом).


Да хоть что угодно. Описанное тобой — лишь дополнительные факты и правила. Я просто хотел показать, что написать код проверки утверждений именно в виде кода, делающего это наилучшим способом для данного утверждения, куда как полезнее, чем это утверждение описывать декларативно для внешнего солвера. Все, что у тебя сейчас есть в виде "естественных" св-в твоей модели мета-информации, надо будет превратить в элементы другой модели, где значения св-в уйдут в таблицы истинности для задания существующих отношений, и т.д. И еще там, где сейчас в реально возникающих ситуациях тебе не нужно будет пользовать до 99% информации, например, далеко не всегда надо даже проверять отношение база/наследник, то для внешней модели тебе придется априори все эти факты грузить, "утяжеляя" модель.

Мне хотелось дать тебе понять, сколько надо сделать для простейшего примера, чтобы ты представил себе объем модели, который надо будет "скормить" внешнему решателю для реальной программы и полноценного языка. Он чудовищен.


VD>Приведения типо и перегрузки уже должны учитываться в моделе которая описывает зависимости между типами.


Само по себе ничего учитываться не будет, разумеется. Ты должен будешь наполнить модель этими правилами-зависимостями. Вот и представь объем мета-информации о модели...

VD>Так нужно ввести сущность "вызов" которая будет описывать вызов функции. Там должны быть ветви для учета перегрузки и т.п.



Ну.... кол-во сущностей как раз неограничено, так же как и кол-во правил под каждую из них.

V>>Интерпретация — это исполнение данных, никогда это быстро не будет, ни на каком волшебном Z3. Надо по-максимуму исполнять код.


VD>То что компиляция при прочих равных быстрее интерпретации — это очевидно уважаемый капитан Очевидность.

VD>Вот только не факт, что прочие равные.

Нативность, в сравнении с дотнетом, не дает ускорения более чем в 3-5 раз, измерено многократно. А чего-то другого "волшебного" в Z3 быть не может. Да, он может быть значительно эффективнее солверов, написанных на Лиспе, например.

А тут всего в 3-5 раз ускорится операция проверки каждого варианта модели, притом что затраты на воссоздание модели — ой какие нешуточные. Почему и сомнительны бенефиты.


V>>Не увидел там движка поиска цели, показывали только решатель, который проверяет модель на корректность.


VD>Плохо смотрел.


Буду признателен, если ткнешь пальцем в доку + пример.


V>>Вряд ли упростит. Там только трансформация всех ваших данных, представляющих компилируемую программу, в модель Z3 — это дополнительное огромное кол-во кода, который сам по себе сложен и будет требовать массу времени для отладки.


VD>Мне нужны рассуждения подкрепленные чем-то на что можно положиться, а не просто мнение в стиле "не взлетит".


Да просто дороговат может получиться эксперимент, в плане трудоемкости. И не факт, что будет успешным. За эту же трудоемкость можно получить внятные данные по профайлингу и понимание узких мест в имеющемся коде.
Re[4]: Z3 theorem prover и вывод типов
От: catbert  
Дата: 05.10.10 19:21
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Меня только настораживает библиотечная реализация этого дела. Т.е. это ты заполнишь данными и вызовешь нечто вроде "реши!"... это же у тебя интерпретатор получится, прямо как в Прологе, а хотелось бы иметь компилированную и оптимизированную версию. То бишь факты пойдут как данные по-любому, а утверждения (самая нагрузочная часть) неплохо бы скомпилировать.


А как, собственно, их можно скомпилировать? Из какого представления в какое?
Re[5]: Z3 theorem prover и вывод типов
От: vdimas Россия  
Дата: 05.10.10 22:49
Оценка:
Здравствуйте, catbert, Вы писали:


V>>Меня только настораживает библиотечная реализация этого дела. Т.е. это ты заполнишь данными и вызовешь нечто вроде "реши!"... это же у тебя интерпретатор получится, прямо как в Прологе, а хотелось бы иметь компилированную и оптимизированную версию. То бишь факты пойдут как данные по-любому, а утверждения (самая нагрузочная часть) неплохо бы скомпилировать.


C>А как, собственно, их можно скомпилировать? Из какого представления в какое?


Из представления в виде данных, по которым работает интерпретатор, в обычный код.
Re[6]: Z3 theorem prover и вывод типов
От: hardcase Пират http://nemerle.org
Дата: 06.10.10 14:42
Оценка:
Здравствуйте, vdimas, Вы писали:

C>>А как, собственно, их можно скомпилировать? Из какого представления в какое?


V>Из представления в виде данных, по которым работает интерпретатор, в обычный код.


Так оно вроде сейчас с первого взгляда так и есть...
/* иЗвиНите зА неРовнЫй поЧерК */
Re[7]: Z3 theorem prover и вывод типов
От: vdimas Россия  
Дата: 07.10.10 08:49
Оценка:
Здравствуйте, hardcase, Вы писали:

V>>Из представления в виде данных, по которым работает интерпретатор, в обычный код.


H>Так оно вроде сейчас с первого взгляда так и есть...


Именно что, и я не уверен, что даже самый быстрый интерпретатор сможет заметно ускорить.
Re[6]: Z3 theorem prover и вывод типов
От: catbert  
Дата: 07.10.10 18:23
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Из представления в виде данных, по которым работает интерпретатор, в обычный код.


Я в Прологе слабо разбираюсь; в моем понимании, правило Пролога — это терм с левой стороны и список термов с правой стороны, на которые движок может заменить терм с левой стороны. Как это скомпилировать в выполняемый код?

Допустим есть такая программа:
father(X, Y) :- parent(X, Y), male(X).
mother(X, Y) :- parent(X, Y), female(X).


Как может выглядеть откомпилированный псевдокод? И каким образом можно его сгенерировать?
Re[7]: Z3 theorem prover и вывод типов
От: vdimas Россия  
Дата: 10.10.10 11:24
Оценка:
Здравствуйте, catbert, Вы писали:

V>>Из представления в виде данных, по которым работает интерпретатор, в обычный код.


C>Я в Прологе слабо разбираюсь; в моем понимании, правило Пролога — это терм с левой стороны и список термов с правой стороны, на которые движок может заменить терм с левой стороны. Как это скомпилировать в выполняемый код?


C>Допустим есть такая программа:

C>
C>father(X, Y) :- parent(X, Y), male(X).
C>mother(X, Y) :- parent(X, Y), female(X).
C>


C>Как может выглядеть откомпилированный псевдокод? И каким образом можно его сгенерировать?


Ну, для начала, 1-арные отношения (факты) можно представить сразу в виде свойств сущностей.
class Entity1 { 
    public bool male { get; }
    public bool female { get; }
}


Но это мелочи. Настоящие вкусности начнутся после некоторой оптимизации. Например, 1-арные отношения могут формировать типы. Что это значит? Это значит, что для сущностей, для которых нет отношений male или female даже можно не проверять соотв. отношения mother/father. Понятие "типа" не обязательно на низком уровне реализовать в виде типа низлежащей подсистемы, например в виде типов .Net, потому как система типов .Net может оказаться недостаточно выразительной, т.е. на низком уровне можно бы реализовать свою подсистему динамической типизации, например оставить в виде указанных флагов. Но, сам типизированный подход позволит выстроить типизированный граф генерируемого кода еще на этапе компиляции, т.е. в статике, произведя заведомые отсечения в большом кол-ве мест, где еще на этапе компиляции видно, что типы не совпадают. Т.е. мы исходим из того, что список утверждений-правил фиксирован до компиляции, имея на руках весь код, требующий генерации, и у нас есть только возможность добавлять факты.

Затем, 2-арные отношения (одни из самых популярных) тоже можно оптимизировать, храня их "встречные" коллекции в каждой из сущностей, вместо общей таблицы.
Ну и, самое главное, логика утверждений из перебора по данным превращается в непосредственные вычисления:
// каждое правило возвращает цепочку утверждений/фактов или null
// (или список цепочек, если ответов несколько)
Iterator<Term> father(Entity x, Entity y) {
    if(x.Male && y.parent_2.Contains(x)) { // или x.parent_1.Contains(y)
        yield return term_male_xxx;   // _xxx и _yyy - некие автогенеренные номера для именования переменных-экземпляров фактов/правил
        yield return term_father_yyy;
    }
}


Если же цепочка вывода не требуется, требуется только проверка, то более простой вариант такой:
(1)
bool father_check_only(Entity x, Entity y) {
    return x.Male && y.parent_2.Contains(x);
}


Сам язык Пролог достаточно беден, ибо описывает практически базовые вещи, но реально-полезный язык мог бы давать хинты, например, чтобы использовать для заведомого малых таблиц линейные списки (массивы), и has-set для остальных.

Двигаемся дальше.
Вот как бы тоже самое выглядело для интерпретатора:
(2)
// Сгенерированный код запроса father(a, b)?
// в обобщенном виде для интерпретатора - всегда список цепочек вывода (т.е. от 0-ля до oo цепочек вывода)
Iterator<Iterator<Term>> father_2_query(object x, object y) { 
    const int relationId_father_2 = xxx;  // ID 2-арного отношения father, известно после этапа обработки исходного текста
    Relation relationFather_2 = relations[relationId_father_2];  // оверхед даже на доступ к каждой таблице отношений

    // проходимся по всем фактам и правилам, задающим отношение
    foreach(Term_2 ruleOrFact in relationFather_2) {
        // вложено итерируем каждую цепочку вывода
        foreach(Iterator<Iterator<Term>> answerChain in ruleOrFact.invoke(a, b))
            foreach(Iterator<Term> answer in answerChain)
                yield return answer;  // разумеется, для некоторого функционального языка
                                      // уже существуют библиотечные ф-ии для "склейки" итераторов,
                                      // но интересно показать, что происходит на самом деле      
    }       
}        

// Утверждение-правило: father(X, Y) :- parent(X, Y), male(X).
// которое достанет из таблицы отношений вышеприведенный код запроса.
// _xxx - автогенеренный порядковый номер правила
Iterator<Iterator<Term>> father_2_xxx(object a, object b) {
    const int relationId_parent_2 = yyy;  // известно после этапа обработки исходного текста
    Relation parent_2 = relations[relationId_parent_2]; 
    
    // проходимся по всем фактам/правилам parent(X, Y)
    foreach(Term_2 ruleOrFactParent in parent_2 ) {
        // проходимся по всем цепочкам вывода parent(X, Y) 
        foreach(Iterator<Iterator<Term>> chainParent in ruleOrFactParent.invoke(a, b)) {
            const int relationId_male_1 = xxx;  // известно после этапа обработки исходного текста
            Relation male_1 = relations[relationId_male_1]; 
    
            // проходимся по всем фактам-правилам male(X)
            foreach(Term_1 termMale in male_1) {
                // проходимся по всем цепочкам-вывода male(X) для каждого факта-правила
                foreach(Iterator<Iterator<Term>> chainMale in termMale.invoke(a)) {
                    // конкатенируем 2 цепочки вывода, т.е. полностью перемножаем два множества
                    foreach(Iterator<Term> parent_N in chainParent) 
                    {
                        yield return parent_N;

                        foreach(Iterator<Term> male_M in chainMale) {
                            yield return male_M;
                    }   
                }
            }
        }  
    }
}


Сравни (1) и (2), представь разницу в быстродействии.

И это еще довольно оптимизировано показано для (2), обычно статически арность в систему типов бай-кода Пролог-машины не вбивают, а аргументы подаются как обычный связанный список, т.е. проверка арности перед каждым вычислением занимает еще больше ресурсов.

Что еще хорошо для компиляции? Это то, что много правил для многих сочетаний фактов можно вычислить заранее. То бишь, вместо кода вычислений сразу писать ответ. В общем, обычное распространение констант для компилируемого языка.

Ну и добавлю — Пролог не очень хорошо подходит для компиллируемости, ибо он был изначально интерпретатором. Для компилируемого образа неплохо бы ввести массу хинтов, например, ввести простейшие типы (целые, дробные, строки) и типизировать ими отношения. Задавать хинты для вида контейнеров, хранящих сами отношения (уже говорил). Давать специальные хинты для тех наборов фактов, которые не будут расширяться в рантайм (compile-only факты). В этом случае compile-time вычисления могут привести к значительной оптимизации генерируемого кода. Ну и диагностика в compile-time получше будет. Например, рекурсивное прямое или косвенное зацикливание на этапе compile-time выявляется легко.

В общем, интересный вариант лежит здесь, жаль что проект довольно сырой: http://mercurylanguage.ucoz.ru/

--------------
Ну и программы надо писать оптимально тоже, не забывать. В нашем примере оптимальнее было бы так:
parent(X) :- father(X).
parent(X) :- mather(X).

Но! Тогда в язык надо было бы вводить констейны, типа:
father(X) 'оператор-constrain' male(X).

Для интерпретатора, работающего целиком в динамике, ввод констрейнов не дал бы никаких бенефитов, т.е. механизм констрейнов можно выразить через систему правил, как в твоем примере. Но для статической компиляции констрейны могут существенно упростить код, уменьшив итоговую мощность перебора.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.