В Lisp код - это данные. Какая польза от этого?
В Lisp любой программный код фактически является действительной структурой данных. Например, это добавляет один и два вместе, но также список из трех элементов.
(+ 1 2)
Какая польза от этого? Что это делает вас невозможным и/или менее элегантным на других языках?
Ответы
Ответ 1
Чтобы сделать вещи немного более четкими в отношении представления кода, учтите, что в каждом коде языка есть данные: все, что вам нужно, это строки. (И, возможно, несколько операций с файлами.) Созерцая, как это поможет вам подражать преимуществам Lisp наличия макросистемы, является хорошим способом просветления. Еще лучше, если вы попытаетесь реализовать такую макросистему. Вы столкнетесь с преимуществами структурированного представления и плоскостности строк, необходимости сначала выполнить преобразования и определить "синтаксические крючки", чтобы указать, где их применять, и т.д.
Но главное, что вы увидите во всем этом, заключается в том, что макросы - это, по сути, удобное средство для перехватчиков компилятора - те, которые подключены к вновь созданным ключевым словам. Таким образом, единственное, что действительно необходимо, - это некоторый способ взаимодействия кода пользователя с кодом компилятора. Плоские строки - один из способов сделать это, но они предоставляют так мало информации о том, что макро-писателю остается задача реализовать парсер с нуля. С другой стороны, вы можете выявить некоторую внутреннюю структуру компилятора, такую как предварительно обработанные деревья AST, но они, как правило, предоставляют слишком много информации, чтобы быть удобной, и это означает, что компилятор должен как-то разбираться в новом синтаксическом расширении, которое вы намерены реализовать. S-выражения являются хорошим решением для последнего: компилятор может анализировать все, поскольку синтаксис является единообразным. Они также являются решением для первого, поскольку они простые структуры с богатой поддержкой языка для их разделения и повторного объединения их по-новому.
Но, конечно, это не конец истории. Например, интересно сравнить простые символические макросы, как в CL, и гигиенические макросы, как и в реализациях Схемы: они обычно реализуются путем добавления дополнительной информации к представленным данным, а это означает, что вы можете делать больше с помощью этих макросистем. (Вы также можете сделать больше с макросами CL, поскольку дополнительная информация также доступна, но вместо того, чтобы сделать ее частью представления синтаксиса, она передавалась как дополнительный аргумент среды макросам.)
Ответ 2
Мой любимый пример... В колледже некоторые мои друзья писали компилятор в Lisp. Таким образом, все их структуры данных, включая дерево разбора, были lisp s-выражениями. Когда пришло время реализовать фазу генерации кода, они просто выполнили свое дерево синтаксического разбора.
Ответ 3
Lisp был разработан для манипуляции символическими данными всех видов. Оказывается, можно также увидеть любую программу Lisp в качестве символических данных. Таким образом, вы можете применить возможности манипуляции Lisp к себе.
Если вы хотите вычислить программы (для создания новых программ, для компиляции программ для машинного кода, для перевода программ из Lisp на другие языки), у вас есть в основном три варианта:
-
используйте строки. Это становится утомительным разбором и неразборчивыми строками все время.
-
используйте деревья синтаксического разбора. Полезно, но становится сложным.
-
используйте символические выражения, например, в Lisp. Программы подготавливаются к знакомым структурам данных (спискам, символам, строкам, номерам и т.д.), А процедуры манипуляции записываются в обычной функциональности, предоставляемой языком.
Итак, что вы получаете с Lisp? Относительно простой способ писать программы, которые управляют другими программами. От макросов до компиляторов есть много примеров этого. Вы также можете легко вставлять языки в Lisp.
Ответ 4
Он позволяет вам писать макросы, которые просто преобразуют одно дерево списков в другое. Простой пример (схема):
(define-syntax and
(syntax-rules ()
((and) #t)
((and thing) thing)
((and thing rest ...) (if thing (and rest ...) #f))))
Обратите внимание, что макро-вызовы просто сопоставляются с предложениями (and)
, (and thing)
и (and thing rest ...)
(в зависимости от арности вызова) и обрабатываются соответствующим образом.
С другими языками макросы должны иметь дело с каким-то внутренним АСТ преобразуемого кода --- иначе не было бы простого способа "видеть" ваш код в программном формате --- и что увеличит трение написания макросов.
В программах Lisp макросы обычно используются довольно часто, именно из-за низкого трения их написания.