Здравствуйте, vdimas, Вы писали:
V>Такой дизайн. ))
Какой такой дизайн?
V>Заметь, я не говорил, что в Лиспе нельзя производить трансформацию AST явным образом, если ты напишешь свой некий такой код.
Тогда зачем ты «противопоставил» их Форту?
V>Я напоминаю, как устроены стандарные максросы Лиспа.
Тогда что значит «подстановочные»?
V>Разве?
V>V>(defmacro foo2 (x)
V> (list 'exp x))
Разве. x — любое S-выражение, либо дерево, либо атом. Обрабатывай как хочешь.
V>В момент компиляции во внутренее представления встречающихся foo2 с аргументом x, де-факто будет происходить подстановка (list 'exp x)) с указанным x.
Потому что ты его так определил.
V>Это обычный подстановочный макрос, который может быть реализован как текстуальный/шаблонный.
Но работает он не с текстом, а с lisp-объектами.
V>На практике в Лиспе он не текстуальный по соображениям эффективности — тело макроса хранится в предкомпилённом виде, т.е. что-то вроде шаблонов С++.
Не вроде.
V>Никакого явного оперирования AST тут нет, как нет оперирования AST в шаблонах С++.
Так ты никак не оперируешь, потому что такой макрос написал. Макрос loop по-твоему тоже никак не оперирует?
(defun bindingp (expr)
(and (= (length expr) 3)
(destructuring-bind (var eq-sym val-expr) expr
(eq eq-sym '=))))
(defmacro my-let (&rest body)
(loop :while (bindingp (first body))
:collect (first (first body)) :into variables
:collect (third (first body)) :into expressions
:do (setf body (rest body))
:finally (return `((lambda ,variables ,@body) ,@expressions))))
(defun main ()
(my-let (x = 1) (y = 2)
(print (+ x y))))
Или что ты подразумеваешь под «оперированием AST»?
V>Эта функциональность идёт в ввиде отдельной библиотеки-интерпретатора.
V>То бишь, это независимая динамическая реализация Схемы, если ты подключаешь библиотеку-интерпретатор в программу.
V>Соответственно, у подключаемого движка-интерпретатора нет никакой связи с твоей программой.
V>В Схеме eval имеет сигнатуру (eval expression environment) где environment необходимо набить ручками перед вызовом eval.
V>Сравнить с Лиспом, в котом eval имеет сигнатуру (eval expression), а environment используется неявным образом текущий.
И что, невозможно выполнить в рантайме, в пользовательском eval Схемы define-syntax?
V>Да просто открываешь доку по макросам Лиспа и Схемы, сравниваешь.
V>Там очевидно.
Тебе сложно перечислить парочку?
V>>>Но это по-прежнему подстановочные/позиционные макросы, без явного манипулирования AST.
_>>Что мешает работать с syntax object в Схеме через syntax-case, например?
V>Это что-то вроде частичного определения шаблонов в С++ — описание неких частных случаев.
V>Механика та же, что и для общего случая.
ничего общего с шаблонами C++. И это не некие частные случаи, а возможность обрабатывать syntax-object явным образом с помощью обычных функций. Это, как раз общий случай, а syntax-rules — частный, упрощённый способ.
V>Да пофик, динамичность никуда не девается.
V>Всё-равно под капотом работает Лисп-машинка.
Как и Схем-машинка в Схеме.
_>>А eval из Схемы никуда не делся.
V>Он прямо по стандарту делся в отдельную библиотеку, которая подключается при надобности.
По которому стандарту и в какую библиотеку?
Вот смотрю, например,
R5RS и не вижу упоминания библиотек.
Вот
R7RS, eval наместе. Про библиотеку не сказано.
V>Идея Схемы была дать возможность исполнять конечный код без Лисп-машинки, без ссылочной семантики в >90% случаев, без динамического построения текущего контекста и поиска в нём символов (даже в компиллируемом Лиспе символы ищутся в текущем контексте динамически в процессе работы программы) и т.д.
Откуда ты взял эту идею? У cons-ячеек в Схеме ссылочная семантика и это используется в SICP.
V>>>Для целей эффективного конечного кода пришлось внести в Лисп несколько ограничений, наверно поэтому назвали другим языком и стандарты Схемы с тех пор развиваются независимо от Лиспа.
_>>Схема не так и не для этого разрабатывалась.
V>Для этого. ))
V>V>Scheme стал первым диалектом Лиспа, применяющим исключительно статические (а не динамические) области видимости переменных
Это не говорит о цели разработки. И к компиляции/интерпретации отношения не имеет.
V>Что исключало необходимость в оперировании динамическим контекстом, позволило производить оптимизацию бинарного представления как в "обычных языках", например, оптимизацию хвостовой рекурсии. Одним словом, Схема дала ср-ва для стирания символов в конечном образе, хотя эти символы живут на этапе компиляции, скажем, в макросах.
SBCL умеет в оптимизацию хвостовой рекурсии. А в Racket есть динамический контекст в виде parameters.
V>Но при надобности не стирать символы — вот вам рядышком прежний динамический eval.
V>>>Вернее, впервые стандарты на семейство Лисп появились в Схеме — именно из-за вносимых ограничений.
_>>С чего ты взял, что из-за них?
V>Для обеспечения указанных св-в программы.
V>Там и выхода-то другого не было.
V>Поставь себя на место разработчиков такой системы, и вопросы отпадут сами собой.
Зачем мне ставить себя на их место, какое это имеет отношение к стандратизации?
V>Например, если эмулировать ООП на Си, то таблицу виртуальных ф-ий и механику диспетчеризацию по ней (с условием корректировки указателя базы для множественного наследования, например) надо описывать явно в такой эмуляции.
Но при использовании CLOS этого не нужно описывать явно. Всё описано в библиотеке. И какая разница, находится это в библиотеке или зашито в рантайм языка?
V>При этом теряются гарантии целостности, в сравнении с С++, т.е. в эмуляции есть возможность описать объект, имеющий некорректный "бинарный лейаут", на котором эмуляция споткнётся в рантайм (в зависимости от реализации такой эмуляции может быть проход по памяти, либо же динамически выкинута ошибка/сигнал). В ООП-языках нет возможность описать объект некорректно, компилятор не позволяет. Даже в динамических, навроде JS или Питона, в них "объект" будет иметь корректный бинарный лейаут, "ошибки структуры объекта" будут сугубо семантические пользовательского уровня.
Какие гарантии целостности теряются в CLOS?
V>В этом абзаце имелись ввиду не встроенные объекты-символы Лиспа (числа, ф-ии и CONS), а эмулируемые "объекты" в парадигме ООП.
И что с того? Следует ли тогда стремиться вообще всё известное впихнуть в рантайм языка?
V>>>Первоклассной сущностью там являются числа/символы, функции и пара CONS(car, cdr), где каждый элемент пары может быть числом, функцией или опять парой (ссылочная семантика для всего перечисленного).
_>>И много что ещё. Перечитай определение, что такое first-class citizen.
V>Сам почитай. ))
V>V>В C и C++ нельзя создавать функции во время выполнения программы, поэтому функции не являются объектами первого класса в этих языках. В то же время указатели на функцию можно передавать в качестве аргумента и возвращать из другой функции, поэтому иногда функции в C++ называют объектами второго класса (англ. second-class object). Тем не менее, в C++ есть понятие функционального объекта (англ. function object), который является объектом первого класса и реализует эквивалентную функциям семантику.
V>Русским по белому, что функциональный объект в С++ — это объект, эмулирующий семантику ф-ий.
V>Структура функторов в С++ известна, с т.з. компилятора — это пользовательский тип неизвестной семантики.
V>Т.е. компилятор умывает руки, просто делает то, что описано программистом.
При чём тут C и C++, когда речь шла о Лиспе? Кроме того непонятно, что автор имел в виду под «созданием функций во время выполнения программы». Что мешает подключить интерпретатор С в программу?