LINUX.ORG.RU

[ФП][срач] Язык программирования Pure


2

4

Pure - динамически типизированный функциональный язык программирования с моделью исполнения, основанной на идее «term rewriting».

  • Синтаксис, бликий к Haskell (включая карринг, срезы, лямбды, сопоставление с образцом, list comprehensions и «as» patterns);
  • Поддерживает модель ленивости, основанную на продолжениях и заимствованную из Alice ML;
  • Допускает функции с побочными эффектами, опциональную аннотацию типов, интерфейсные типы
  • Поддержка рациональных, комплексных чисел, встроенный RegEx;
  • Содержит спектр встроенных структур данных, таких как: списки, туплы, векторы, матрицы и ленивые последовательности;
  • Интеграция с GSL;
  • Поддерживает полноценную систему гигиенических макросов;
  • Лёгкий FFI с сишными библиотеками;
  • Реализация под LLVM под GNU/Linux, Mac OS X, FreeBSD и Windows;
  • Из коробки поддержка компиляции в нативный код;
  • В комплекте идёт набор батареек для разнообразных нужд.

Про производительность заявляется следующее:

The Pure interpreter is able to achieve very good performance, offering execution speeds in the same ballpark as good Lisp interpreters. It seems to be one of the fastest implementations of term rewriting as a programming language right now, and is certainly good enough for most programming tasks except maybe the most demanding number crunching applications (and even these can be tackled by interfacing to Fortran or C).

Язык активно развивается, последняя версия документации датирована 8 января, автор постоянно пилит своё детище, активно идёт на контакт и охотно принимает и рассматривает любые предложения.

Ссылка на страницу проекта: http://code.google.com/p/pure-lang/
Ссылка на документацию (pdf): http://docs.pure-lang.googlecode.com/hg/puredoc.pdf
Ссылка на пример полной реализации АВЛ-деревьев: http://pure-lang.googlecode.com/hg/pure/examples/avltree.pure
Репозиторий (лицензия LGPLv3): hg clone https://code.google.com/p/pure-lang/

★★★★★
Ответ на: комментарий от anonymous

А кто говорил, что он единственно верный?

В конце вот этого твоего поста опять есть намёк на то что он, по крайней мере, «лучше», этот подход.

Я тебе дал реальную задачу - собрать простейшее выражение

Это не реальная задача - как будто ежедневная практика состоит из собирания подобных простых выражений. Реальная задача это что-то народно-хозяйственное.

у тебя из-за отсутствия гомоиконности это вылилось в ручную сборку АСТ с кучей кода

Пошёл посмотрел - 40 знаков.

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

Ты просто уверовал, что эти средства из схемы более высокоуровневые чем что-то другое. То что я там описал - это чисто ML-ный подход к вещам, макросы тут вообще ни при чём.

В лиспе мы просто берем и пишем макросы на каждый чих.

При этом, есть пишущие лисперы, которые не разделяют такую точку зрения (против «писать макросы на каждый чих»).

В ЯП с гомоиконным синтаксисом подобных проблем можно легко избежать. Так ясно?

Ок. Вообще, гомоиконность это когда вызов макроса не отличается от вызова функции? Мог бы быть haskell/ml-подобный язык гомоиконным? Если в нём вызов макроса отличается от вызова функции видом скобок, например (пусть - {foo ... code ... [code ...] ...} вместо (foo ... code ... (code ...) ...), вокруг foo - засахаренный код, внутри foo - засахаренный код, цитируемый с помощью [...]).

Вот в том и дело, что «по назначению» в ЯП с гомоиконным синтаксисом - это на порядок более широкое множество задач, чем «по назначению» в ЯП с негомоиконным синтаксисом. Уже хрен знает сколько пытаюсь донести до тебя эту мысль, но никак не доходит.

Вот опять слова о превосходстве одного инструмента над другим.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

При этом, есть пишущие лисперы, которые не разделяют такую точку зрения (против «писать макросы на каждый чих»).

Который от xml/xslt через cl пришел к православности js и пропагандирует web-технологии как вершину ui-строительства?

antares0 ★★★★
()
Ответ на: комментарий от quasimoto

В конце вот этого твоего поста опять есть намёк на то что он, по крайней мере, «лучше», этот подход.

Какой еще подход? Не шла речь нигде ни о каких подходах, речь шла об одном конкретном инструменте и о том, что в определенных ситуациях (ЯП с гомоиконным синтаксисом) инструмент намного более эффективен, чем в других. Это ты себе понапридумывал уже какие-то подходы и прочую чепуху.

Это не реальная задача - как будто ежедневная практика состоит из собирания подобных простых выражений.

Ну правильно - реальная задача состоит из собирания намного более сложных выражений. Каждое из которых является композицией вот таких вот простых.

Пошёл посмотрел - 40 знаков.

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

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

Это, конечно, не факт, что они более высокоуровневые чем что-то другое, но пока что никаких претендентов на хотя бы равное по уровню средство никому неизвестно (речь о средствах для написания макросов, естественно, а не о каких-то других, не имеющих никакого отношения к теме).

То что я там описал - это чисто ML-ный подход к вещам, макросы тут вообще ни при чём.

Что значит «макросы не при чем», если речь шла _только_ о макросах и ни о чем больше? Как этот ML-ный подход к вещам относится к теме разговора, и, раз не относится, то зачем он вообще был упомянут?

При этом, есть пишущие лисперы, которые не разделяют такую точку зрения (против «писать макросы на каждый чих»).

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

Ок. Вообще, гомоиконность это когда вызов макроса не отличается от вызова функции?

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

Мог бы быть haskell/ml-подобный язык гомоиконным?

Мог бы, конечно.

Вот опять слова о превосходстве одного инструмента над другим.

Еще раз, мы имеем два инструмента - макросистема в гомоиконном ЯП и макросистема в негомоиконном ЯП (по крайней мере я рассматриваю именно эти два, чего ты там рассматриваешь я не в курсе). И ты _сам_ лично указал, что макросистема в негомоиконном ЯП применима весьма ограничено (и в куче юзкейсов мы, там где можно было бы использовать макросы, применяем вместо них альтернативные инструменты типа АТД, ФВП и так далее), а теперь ты же и споришь с этим фактом? Определись уже.

anonymous
()
Ответ на: комментарий от anonymous

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

Тогда ладно, но если, как было сказано, ЯП с конкретным синтаксисом (вроде ML/Haskell) может быть, в принципе, гомоиконным, то изначальный наезд на инфикс был некорректным.

Что значит «макросы не при чем», если речь шла _только_ о макросах и ни о чем больше? Как этот ML-ный подход к вещам относится к теме разговора, и, раз не относится, то зачем он вообще был упомянут?

Это был пример реальной задачи (конфигурирование работающей системы с помощью внешних файлов конфигурации, написанных на неком DSL), который в лиспах естественно решается с помощью макро-системы, но в ML-ях естественно решается другими средствами, без надобности привлечения макро-системы.

Нет, это когда сам код является АСТ.

Вот с точки зрения TH хаскельный код _является_ AST. По какой причине TH это не гомоиконная система тогда? Высокоуровневые define syntax rules это уже следствие для гомоиконного языка. Причина - гомоиконность. Если способ вызова макросов не важен - почему TH нельзя считать гомоиконным?

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Тогда ладно, но если, как было сказано, ЯП с конкретным синтаксисом (вроде ML/Haskell) может быть, в принципе, гомоиконным, то изначальный наезд на инфикс был некорректным.

Ну да, если взять ЯП с негомоиконным синтаксисом и поменять его на гомоиконный (что предполагает исключение инфикса), то ЯП станет гомоиконным. Но с какой стати наезд некорректен?

Это был пример реальной задачи (конфигурирование работающей системы с помощью внешних файлов конфигурации, написанных на неком DSL), который в лиспах естественно решается с помощью макро-системы, но в ML-ях естественно решается другими средствами, без надобности привлечения макро-системы.

Ну и какое это имеет отношение к теме разговора?

Вот с точки зрения TH хаскельный код _является_ AST.

Не является, конечно же. Какой АСТ задает код «1 op 2 op 3», если неизвестно, что такое op (то есть это может быть оператор любой ассоциативности, или функция или вообще константа)? Для того, чтобы код являлся АСТ нужно, как минимум, прямое соответствие.

По какой причине TH это не гомоиконная система тогда?

По той причине, что хаскельный код не является АСТ.

Высокоуровневые define syntax rules это уже следствие для гомоиконного языка. Причина - гомоиконность.

Ну да. Но этого недостаточно, конечно, если говорить о лиспе - нужно еще, чтобы любая осмысленная синтаксическая единица, не являющаяся атомом, была взята в скобки. Например, в макросе loop это нарушается - по-этому генерить макросы, раскрывающиеся в loop, будет неудобно.

Если способ вызова макросов не важен - почему TH нельзя считать гомоиконным?

Потому что код отделен от АСТ. Вот возьмем выражение ((+) 1 2), ему в хаскеле соответствует нечто вроде (App (Function +) (Lit 1) (Lit 2)). Чтобы хаскель был гомоиконным ЯП, код, представляющий АСТ, должен совпадать с АСТ, то есть АСТ (App (Function +) (Lit 1) (Lit 2)) должно задаваться кодом "(App (Function +) (Lit 1) (Lit 2))". Для лиспа это правило выполняется, соответственно, лисп - гомоиконный ЯП. А хаскель - нет.

anonymous
()
Ответ на: комментарий от quasimoto

пример реальной задачи (конфигурирование работающей системы с помощью внешних файлов конфигурации, написанных на неком DSL), который в лиспах естественно решается с помощью макро-системы, но в ML-ях естественно решается другими средствами, без надобности привлечения макро-системы.

Давно ждал, чтобы кто-то это сказал.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Какой АСТ задает код «1 op 2 op 3», если неизвестно, что такое op (то есть это может быть оператор любой ассоциативности, или функция или вообще константа)?

А такого кода в хаскеле и не может быть (синтаксически невалидная конструкция). Может быть (op (op 1 2) 3), (op 1 (op 2 3)) или (1 `op` 2 `op` 3).

Чтобы хаскель был гомоиконным ЯП, код, представляющий АСТ, должен совпадать с АСТ, то есть АСТ (App (Function +) (Lit 1) (Lit 2)) должно задаваться кодом "(App (Function +) (Lit 1) (Lit 2))".

Ну и это уже лисп. Интересно про гомоиконный не лисп.

Если представления кода и AST изоморфны, и есть возможность _из языка_ использовать функции задающие изоморфизм, т.е. quote : код -> AST и splice : AST -> код, то это и будет гомоиконный язык. Если при этом представления кода и AST совпадают, то это уже лисп.

У хаслеля - представление кода (которое недоступно, например, HsExpr в GHC) не совпадает с представление AST (Exp из TH), так что это не лисп. Более того, представление AST не замкнуто - с помощью TH можно разбирать / собирать хаскель-код, но нельзя собирать TH код, т.е. в Exp, Dec, Type и т.п. из TH нет конструкторов для самого TH. Так что TH не гомоиконная система.

При этом сами представления кода - HsExpr в GHC или Exp из haskell-src-exts, - замкнуты относительно конструкций TH, т.е. то как TH реализовано в GHC - оно там просто недореализовано до (формальной) гомоиконности.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Если при этом представления кода и AST совпадают, то это уже лисп.

* и являются s-выражениями.

quasimoto ★★★★
()
Ответ на: комментарий от tailgunner

Давно ждал, чтобы кто-то это сказал.

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

Жаль, конечно - раньше одним read'ом макрочудеса неописуемые творились.

mv ★★★★★
()
Ответ на: комментарий от quasimoto

А такого кода в хаскеле и не может быть (синтаксически невалидная конструкция). Может быть (op (op 1 2) 3), (op 1 (op 2 3)) или (1 `op` 2 `op` 3).

Ну не суть важно, пусть будет (1 `op` 2 `op` 3)

Ну и это уже лисп.

Нет, это не лисп, это ЯП с гомоиконным синтаксисом.

Интересно про гомоиконный не лисп.

Ну придумай гомоиконный не-лисп, кто тебе мешает? :)

Если представления кода и AST изоморфны, и есть возможность _из языка_ использовать функции задающие изоморфизм, т.е. quote : код -> AST и splice : AST -> код, то это и будет гомоиконный язык.

Нет, если существует изморфизм и есть взможность использовать задающие его ф-и - то это _любой_ ЯП. Потому что в любом ЯП есть парсер, который по коду строит АСТ, задавая подобный изоморфизм. И в любом ЯП мы можем реализовать этот парсер в виде отдельной библиотеки, которая позволит строить АСТ из этого языка. Но с гомоиконностью это, конечно, ничего общего не имеет.

Если при этом представления кода и AST совпадают, то это уже лисп.

Нет, это как раз и есть гомоиконный ЯП. Для меня вопрос существования гомоиокнного не-лиспа, вообще говоря, открыт. Но я вполне допускаю, может быть и так, что гомоиконный ЯП обязательно должен быть лиспом (точнее иметь синтаксис лиспа - ведь если мы в хаскеле сделаем синтаксис лиспа, то это все же будет более хаскель, чем лисп :)). В любом случае, это все вопрос определений - что считать гомоиконностью. Это к обсуждению не относится - я изначально под гомоиконностью подразумевал именно тождество кода-АСТ, если тебе так удобнее, можешь везде в моих постах заменить «ЯП гомоиконный» на «в ЯП код сам по себе является АСТ». Спорить же до усрачки о терминологии я смысла никакого не вижу.

Более того, представление AST не замкнуто - с помощью TH можно разбирать / собирать хаскель-код, но нельзя собирать TH код, т.е. в Exp, Dec, Type и т.п. из TH нет конструкторов для самого TH. Так что TH не гомоиконная система.

Вот этого я не знал. Этот момент как раз относится к вызовам макросов - если макровызовы соответствуют функциональным, то замкнутость получается автоматом. Но я гомоиконность все же понимаю в описанном выше смысле (то есть «код и данные имеют одинаковое представление» - причем это и есть общепринятое определение) - и там тоже замкнутость получается автоматом.

anonymous
()
Ответ на: комментарий от anonymous

Ну не суть важно, пусть будет (1 `op` 2 `op` 3)

Тогда [| 1 `$op` 2 `$op` 3 |] - в текущем TH не работает, но это технический issue. Расстановка скобок аппликаций в хаскеле лево-ассоциативна - a b c == ((a b) c), a `f` b `f` c == ((a `f` b) `f` c) (f - аргумент). Короче, InfixE - часть AST, также как LetE или TupE - разные [| let x = $a ; y = $b in $z |] и [| ($a, $b) |] ведь работают, хотя тут миксфикс и инфикс. Вложенные вещи вроде [| ... $( ... [| ... |] ... ) ... |] тоже работают.

Нет, если существует изморфизм и есть взможность использовать задающие его ф-и - то это _любой_ ЯП.

«Функции задающие изоморфизм» это в математическом смысле (изоморфизм множеств это две таких-то функции, изо-стрелка это две таких-то стрелки и т.п.), с точки зрения языка это будет часть AST, т.е. у типов Exp и ExpMeta будут конструкторы

Quote :: Exp -> ExpMeta
Splice :: ExpMeta -> Exp? -> Exp

это часть грамматики, которая также требует себе семантических атрибутов (по сути, реализации expand-ера).

Нет, это как раз и есть гомоиконный ЯП.

Точно, ExpMeta = Exp - гомоиконный. Если при этом ещё Exp = SExp, то лисп (splice редуцируется в аппликацию, остаётся только quote).

Для меня вопрос существования гомоиокнного не-лиспа, вообще говоря, открыт.

Тут упомянули Io, Rebol и Factor. Ещё есть Dylan (CL с обрезанными ногтями :)), в Nemerle и F# ещё что-то есть. Но для меня тоже открыт вопрос.

Но я вполне допускаю, может быть и так, что гомоиконный ЯП обязательно должен быть лиспом

Ну вот возьмём

data Expression
  = Lit Literal
  | Var Variable
  | Lam [Variable] Expression
  | App Expression [Expression]
  -- sugar, sugar, etc.
  | Quote Expression
  | Splice Expression [Expression]

и будем использовать в конкретном синтаксисе _разные_ нотации для аппликаций, лямбд, цитирования, вставки и других специальных форм. Потом добавим

expand :: Expression -> Expression
eval :: Expression -> Expression
-- ^ Связаны мета-циклически.

Expression замкнут, но уже не s-выражения, мета-циклическая пара eval и expand присутствует. Синтаксическая форма <quote> <expr> при этом даст _объект_ AST, с которым можно будет совершать любые действия в самом языке (в духе аккессоров и конструкторов, но уже не car, cdr и cons, а тех, которые задаются этим ADT, более высокоуровневое конструирование и паттерн-матчинг тоже надстраиваются).

Вообще, с чему бы типу обычных деревьев играть какую-то выделенную роль, тип более богатый конструкторами, в принципе, должен являться обобщением, позволять замыкание относительно quote/splice и возможность реализовать мета-циклические expand/eval.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

но уже не car, cdr и cons, а тех, которые задаются этим ADT

В лиспах car, cdr, cons и некоторые другие функции для s-выражений это примитивы, так что в этом примере конструкторы, аккессоры и прочие необходимые функции связанные с Expression станут примитивами языка.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

типу обычных деревьев

Хотя, там точечные пары, которыми представляются кроме деревьев ещё и некоторые графы-не-деревья. Но когда ими представляется код, а не данные, то это субтип - s-выражения, деревья символов.

Есть одна старая статья - критика SICP на тему использования точечных пар для целей представления ADT (утверждается, что в ML всё няшне). Конечно, в R6RS и CL появились нормальные структуры, объединения, конструкторы, аккессоры и возможность написать компилятор паттерн-матчинга на макросах, но AST всё ещё кодируется s-выражениями ;)

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Тогда [| 1 `$op` 2 `$op` 3 |] - в текущем TH не работает

Nuff said.

Расстановка скобок аппликаций в хаскеле лево-ассоциативна - a b c == ((a b) c), a `f` b `f` c == ((a `f` b) `f` c) (f - аргумент).

И что произойдет, когда окажется, что f - правоассоциативен?

«Функции задающие изоморфизм» это в математическом смысле (изоморфизм множеств это две таких-то функции, изо-стрелка это две таких-то стрелки и т.п.), с точки зрения языка это будет часть AST, т.е. у типов Exp и ExpMeta будут конструкторы

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

то лисп (splice редуцируется в аппликацию, остаётся только quote).

Куда он редуцируется и с какой стати?

Тут упомянули Io, Rebol и Factor. Ещё есть Dylan (CL с обрезанными ногтями :)), в Nemerle и F# ещё что-то есть. Но для меня тоже открыт вопрос.

Ну Dylan, Nemerle и F# явно негомоиконные, про IO с Rebol'ом ничего сказать не могу, разве что Factor остается.

Вообще, с чему бы типу обычных деревьев играть какую-то выделенную роль, тип более богатый конструкторами, в принципе, должен являться обобщением, позволять замыкание относительно quote/splice и возможность реализовать мета-циклические expand/eval.

Угу, но только это все никак не связано с гомоиконностью. Вот в том же Racket, например, АСТ представляется в виде syntax object, а не в виде списков - важно, что эти syntax object полностью наследуют структуру кода (то есть являются надстройкой над списками, благодаря чему все, что можно делать со списками, искаробки можно делать и с syntax object точно теми же способами), а какие там аксесоры и прочая чушь - это не важно, это просто теоретические моменты, которые на практике ничего не значат.

Expression замкнут, но уже не s-выражения, мета-циклическая пара eval и expand присутствует. Синтаксическая форма <quote> <expr> при этом даст _объект_ AST, с которым можно будет совершать любые действия в самом языке (в духе аккессоров и конструкторов, но уже не car, cdr и cons, а тех, которые задаются этим ADT, более высокоуровневое конструирование и паттерн-матчинг тоже надстраиваются).

Понимаешь, ты говоришь о теоретической возможности, а я о практическом использовании. Я пытаюсь тебе объяснить, что если задачу можно решить не менее чем за сто миллионов лет, то на практике это ничем не отличается от задачи, которую нельзя решить вовсе, ты же мне в ответ говоришь - но ведь теоретическое решение есть, expand/eval наличествуют, надстроить теоретически можно бла бла бла...

eval :: Expression -> Expression

eval :: Expression -> Datum - вот так.

anonymous
()
Ответ на: комментарий от quasimoto

и будем использовать в конкретном синтаксисе _разные_ нотации для аппликаций, лямбд, цитирования, вставки и других специальных форм.

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

anonymous
()
Ответ на: комментарий от quasimoto

но AST всё ещё кодируется s-выражениями

Ну так никто еще не придумал никаких более удобных способов кодирования. Напиши такую же простую и вместе с тем полноценную грамматику, как в своем прошлом посте, но так, чтобы в итоге у тебя не получилось с-выражений :)

anonymous
()
Ответ на: комментарий от anonymous

И что произойдет, когда окажется, что f - правоассоциативен?

infixr 5 `f`

будут справа скобки. В случае если f - аргумент, а не top-level функция:

foo f a b = a `f` b

тогда слева.

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

Это не функции, это Quote / Splice в виде конструкторов AST.

Куда он редуцируется и с какой стати?

В AST нет конструктора splice. Есть quote. splice это просто apply когда голова - символ макроса.

а какие там аксесоры и прочая чушь

ADT (~ AST у нас тут) и его поведение задаётся сигнатурами конструкторов, задаёт поведение и сигнатуры аккессоров. Это ключевые моменты. Но тем, кто не любит думать в data driven терминах так не кажется, я смотрю :)

eval :: Expression -> Datum - вот так.

Нет, eval замкнут. Datum, или Value, содержится в Expression. Например, literals, lambdas, quotes - values в expression, applications и variables - нет. Вынести, т.е. агрегировать, values при наличии конструктора quote не получится (да это и просто неудобно / не нужно).

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

Я пытаюсь тебе объяснить

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

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Вынести, т.е. агрегировать, values при наличии конструктора quote не получится

Нет, ну можно, но Value и Exp будут взамо-рекурсивными типами. Не нужно.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

будут справа скобки.

Так где они будут после построения АСТ (то есть до того, как мы узнали ассоциативность)?

Это не функции, это Quote / Splice в виде конструкторов AST.

Конструкторы - это функции. И, кстати, Quote/Eval, а не Quote/Splice.

В AST нет конструктора splice. Есть quote. splice это просто apply когда голова - символ макроса.

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

ADT (~ AST у нас тут) и его поведение задаётся сигнатурами конструкторов, задаёт поведение и сигнатуры аккессоров. Это ключевые моменты.

AST задается его грамматикой, а в виде каких ADT оно будет представлено - это дело десятое, ведь всегда можно сделать враппер к другому эквивалентному представлению. Так что здесь следует говорить о классе представлений АДТ, который соответствует данной грамматике. Выбор конкретного представителя класса ничего не меняет - как и всегда при выборе одного объекта из совокупности изоморфных :)

Нет, eval замкнут.

Конечно замкнут, но тип у него Expression -> Datum, иначе это не евал, а какая-то профанация.

Datum, или Value, содержится в Expression.

Нет, наоборот - Expression содержится в Datum. То есть код - некоторый частный случай данных, не наоборот.

Вынести, т.е. агрегировать, values при наличии конструктора quote не получится (да это и просто неудобно / не нужно).

коненчо, не получится. Метацикличность нельзя формально выразить :)

anonymous
()
Ответ на: комментарий от quasimoto

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

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

anonymous
()
Ответ на: комментарий от anonymous

Метацикличность нельзя формально выразить

Это безусловно очень важное преимущество Лиспа, ведь оно так повышает ЧСВ лиспокодера.

stack_protector
()
Ответ на: комментарий от anonymous

Так где они будут после построения АСТ (то есть до того, как мы узнали ассоциативность)?

Попробую так - если у нас функция отдаётся аргументом, то никакой ассоциативности у неё быть не может (поэтому те штуки в TH не работают), для неё будет простой AST с AppE. Писать [| a `f` b |] можно только для объявленных функций (возможно, указана ассоциативность), [| a ? b |] - для объявленных операторов (тоже).

Конструкторы - это функции. И, кстати, Quote/Eval, а не Quote/Splice.

Это особого рода функции (у них единственное правило rewriting - identity). Может, я вообще считаю за функции не identiчные rewriting rules. Нет не eval, почему? Eval как раз обычная функция, это _точно_ не конструктор (сам посуди). В лиспах нету аналога конструктора splice.

Так ведь и quote в данном случае вырожден

Откроем любой стандарт - спец. форма quote есть, самая обычная, ни во что не вырожденная.

но тип у него Expression -> Datum

Пусть так.

Нет, наоборот - Expression содержится в Datum

Они взаиморекурсивны - содержатся в друг друге (агрегируют друг друга).

data Val
  = Lit Lit
  | Lam [Exp] Exp
  | Quote Exp

data Exp
  = Var Var
  | Val Val
  | App Exp [Exp]
  | Splice Exp [Exp]
quasimoto ★★★★
()
Ответ на: комментарий от anonymous

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

Обоснуй выделенность паттерна Compiler, сошлись на Турчина :)

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Обоснуй выделенность паттерна Compiler, сошлись на Турчина :)

Зачем обосновывать какую-то выделенность и на кого-то там ссылаться?

anonymous
()
Ответ на: комментарий от quasimoto

для неё будет простой AST с AppE. Писать [| a `f` b |] можно только для объявленных функций (возможно, указана ассоциативность), [| a ? b |] - для объявленных операторов (тоже).

Ну и какой конкретно будет АСТ в случае [| 1 `op` 2 `op` 3 |]?

Это особого рода функции

Функция особого рода - тоже функция.

Может, я вообще считаю за функции не identiчные rewriting rules.

Ты лично можешь все что угодно считать, но мы как будто находимся в семантическом поле планеты Земля, а не в твоей воображаемой реальности, так?

Нет не eval, почему? Eval как раз обычная функция, это _точно_ не конструктор (сам посуди).

Ну да, не конструктор. А кто тебе сказал, что Quote - конструктор? Просто тип quote: Datum -> Expression, тип eval: Expression -> Datum. Между ними как бы двойственность. А splice = expand (Expression -> Expression). Ты просто с какого-то перепугу решил сделать их конструкторами, хотя они семантически конструкторами быть не могут...

Откроем любой стандарт - спец. форма quote есть, самая обычная, ни во что не вырожденная.

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

Они взаиморекурсивны - содержатся в друг друге (агрегируют друг друга).

Проблема в том, что они содержат друг друга как обычные объединения (не размеченные), откуда следует val = expression, что и есть определение гомоиконности :)

То есть такой пример:

(define (f x) x) ; просто определили какую-то функцию, не важно какую
`(1 2 ,f)
Так вот `(1 2 ,f) задает определенный АСТ (кстати, в лиспе АСТ это именно консы, а не деревья, то есть АСТ может быть и зациклен сам на себя) и третий элемент этого АСТ - это именно _функция_. Не символ f, а именно функция, которую мы получаем при (eval 'f). Ну да, ты можешь сказать, что это все рантайм-значения, по-этому другой пример:
#lang racket
(begin-for-syntax
  (define (f x) x))

(define-syntax (macro stx)
  #`(list 1 2 #,f))
->
Добро пожаловать в DrRacket, версия 5.2.0.1--2011-11-01(98d8acf/a) [3m].
Язык: racket [выбранный].
> f
. . collects\racket\private\more-scheme.rkt:268:2: reference to an identifier before its definition: f
> (macro)
'(1 2 #<procedure:f>)
> 
то есть двойственность данные-код несколько глубже, чем ты себе представляешь и так как ты пытаешься ее через АТД описать нельзя.

anonymous
()
Ответ на: комментарий от anonymous

Ну и какой конкретно будет АСТ

runQ [| 1 `op` 2 `op` 3 |]
-- >
InfixE (Just (InfixE (Just (LitE (IntegerL 1))) (VarE op) (Just (LitE (IntegerL 2))))) (VarE op) (Just (LitE (IntegerL 3)))

то есть двойственность данные-код несколько глубже, чем ты себе представляешь и так как ты пытаешься ее через АТД описать нельзя.

Буду иметь это в виду.

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

Так и что будет, если потом внезапно выяснится, что op был правоассоциативен?

f = (+)

t1 = runQ [| 1 `f` 2 `f` 3 |]
-- InfixE (Just (InfixE (Just (LitE (IntegerL 1))) (VarE Main.f) (Just (LitE (IntegerL 2))))) (VarE Main.f) (Just (LitE (IntegerL 3)))

infixr 7 `g`
g = (*)

t2 = runQ [| 1 `g` 2 `g` 3 |]
-- InfixE (Just (LitE (IntegerL 1))) (VarE Main.g) (Just (InfixE (Just (LitE (IntegerL 2))) (VarE Main.g) (Just (LitE (IntegerL 3)))))

t3 = runQ [| \a b c -> a * b + c |]
-- LamE [VarP a_0,VarP b_1,VarP c_2] (InfixE (Just (InfixE (Just (VarE a_0)) (VarE GHC.Num.*) (Just (VarE b_1)))) (VarE GHC.Num.+) (Just (VarE c_2)))

t4 = runQ [| \op -> 1 `op` 2 `op` 3 |]
-- LamE [VarP op_0] (InfixE (Just (InfixE (Just (LitE (IntegerL 1))) (VarE op_0) (Just (LitE (IntegerL 2))))) (VarE op_0) (Just (LitE (IntegerL 3))))

t5 op = runQ [| $op ($op 1 2) 3 |]
-- t5 [| (+) |]
-- AppE (AppE (VarE GHC.Num.+) (AppE (AppE (VarE GHC.Num.+) (LitE (IntegerL 1))) (LitE (IntegerL 2)))) (LitE (IntegerL 3))
quasimoto ★★★★
()
Ответ на: комментарий от anonymous

В первом приближении можно считать, что [| ... |] это как `( ... ), а [| ... $(...) ... |] - как `( ... ,( ... ) ... ).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

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

anonymous
()
Ответ на: комментарий от anonymous

вопрос же заключался в том, что будет, если сперва построен левоассоциативный АСТ, а потом окажется, что оператор в этом АСТ правоассоциативен или мы поменяли ассоциативность, например.

Такого не может случится (такие свойства у самого haskell-98).

quasimoto ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.