Пользователи Lisp называют Lisp как программируемый язык программирования . Используется для символьных вычислений - для вычислений с символами.
Макросы - это только один из способов использования символической вычислительной парадигмы. Более широкое видение заключается в том, что Lisp предоставляет простые способы описания символических выражений: математических терминов, логических выражений, итерационных операторов, правил, описаний ограничений и многого другого. Макросы (преобразования исходных форм Lisp) являются лишь одним из приложений символьных вычислений.
В этом есть определенные аспекты: если вы спросите о «переопределении» языка, то переопределение строго будет означать переопределение существующего языкового механизма (синтаксис, семантика, прагматика). Но есть также расширение, встраивание, удаление языковых возможностей.
В традиции Лисп было много попыток предоставить эти функции. Лиспский диалект и определенная реализация могут предлагать только их подмножество.
Несколько способов переопределить / изменить / расширить функциональность в соответствии с основными реализациями Common Lisp:
Синтаксис s-выражения . Синтаксис s-выражений не фиксирован. Считыватель (функция READ) использует так называемые таблицы чтения для указания функций, которые будут выполняться при чтении символа. Можно изменять и создавать таблицы чтения. Это позволяет вам, например, изменить синтаксис списков, символов или других объектов данных. Можно также ввести новый синтаксис для новых или существующих типов данных (например, хеш-таблиц). Также возможно полностью заменить синтаксис s-выражения и использовать другой механизм синтаксического анализа. Если новый синтаксический анализатор возвращает формы Lisp, для интерпретатора или компилятора не требуется никаких изменений. Типичным примером является макрос чтения, который может читать выражения инфикса. В таком макросе чтения используются инфиксные выражения и правила приоритета для операторов. Макросы чтения отличаются от обычных макросов: макросы чтения работают на уровне символов синтаксиса данных Lisp.
замена функций . Функции верхнего уровня связаны с символами. Пользователь может изменить эту привязку. В большинстве реализаций есть механизм, позволяющий сделать это даже для многих встроенных функций. Если вы хотите предоставить альтернативу встроенной функции ROOM, вы можете заменить ее определение. Некоторые реализации вызовут ошибку, а затем предложат возможность продолжить изменение. Иногда это необходимо, чтобы разблокировать пакет. Это означает, что функции в целом могут быть заменены новыми определениями. Есть ограничения к этому. Одним из них является то, что компилятор может встроить функции в коде. Чтобы увидеть эффект, нужно перекомпилировать код, который использует измененный код.
функции консультирования . Часто хочется добавить некоторое поведение к функциям. Это называется «консультирование» в мире Lisp. Многие реализации Common Lisp предоставляют такую возможность.
нестандартные пакеты . Пакеты группируют символы в пространствах имен. Пакет COMMON-LISP является домом для всех символов, которые являются частью стандарта ANSI Common Lisp. Программист может создавать новые пакеты и импортировать существующие символы. Таким образом, вы можете использовать в своих программах пакет EXTENDED-COMMON-LISP, который предоставляет больше или другие возможности. Просто добавив (IN-PACKAGE "EXTENDED-COMMON-LISP"), вы можете начать разработку, используя собственную расширенную версию Common Lisp. В зависимости от используемого пространства имен используемый вами диалект Lisp может выглядеть незначительно или даже радикально отличаться. В Genera на Lisp Machine есть несколько диалектов Lisp, расположенных рядом друг с другом: ZetaLisp, CLtL1, ANSI Common Lisp и Symbolics Common Lisp.
CLOS и динамические объекты. Common Lisp Object System поставляется со встроенными изменениями. Протокол мета-объектов расширяет эти возможности. Сам CLOS может быть расширен / переопределен в CLOS. Вы хотите другое наследство. Напишите метод. Вы хотите разные способы хранения экземпляров. Напишите метод. Слоты должны иметь больше информации. Обеспечить класс для этого. Сам CLOS спроектирован так, что он способен реализовать целый «регион» различных объектно-ориентированных языков программирования. Типичными примерами являются добавление таких вещей, как прототипы, интеграция с системами внешних объектов (например, Objective C), добавление стойкости, ...
Лисп формы . Интерпретация форм Lisp может быть переопределена с помощью макросов. Макрос может проанализировать исходный код и изменить его. Существуют различные способы управления процессом трансформации. Сложные макросы используют обходчик кода, который понимает синтаксис форм Lisp и может применять преобразования. Макросы могут быть тривиальными, но также могут быть очень сложными, как макросы LOOP или ITERATE. Другими типичными примерами являются макросы для встроенного SQL и генерация встроенного HTML. Макросы также могут использоваться для перемещения вычислений во время компиляции. Поскольку компилятор сам является программой на Лиспе, во время компиляции могут выполняться произвольные вычисления. Например, макрос Lisp может вычислить оптимизированную версию формулы, если во время компиляции известны определенные параметры.
Символы . Common Lisp предоставляет макросы символов. Макросы символов позволяют изменить значение символов в исходном коде. Типичный пример: (with-slots (foo) bar (+ foo 17)) Здесь символ FOO в источнике, заключенном в WITH-SLOTS, будет заменен вызовом (bar-value bar 'foo).
оптимизации , с помощью так называемых макросов компилятора можно обеспечить более эффективные версии некоторых функций. Компилятор будет использовать эти макросы компилятора. Для пользователя это эффективный способ программирования оптимизации.
Обработка условий - обработка условий, возникающих в результате определенного использования языка программирования. Common Lisp предоставляет расширенный способ обработки ошибок. Систему условий также можно использовать для переопределения языковых функций. Например, можно обрабатывать неопределенные функциональные ошибки с помощью самописанного механизма автозагрузки. Вместо посадки в отладчике, когда Lisp видит неопределенную функцию, обработчик ошибок может попытаться автоматически загрузить функцию и повторить операцию после загрузки необходимого кода.
Специальные переменные - добавить привязки переменных в существующий код. Многие диалекты Lisp, такие как Common Lisp, предоставляют специальные / динамические переменные. Их значение ищется во время выполнения в стеке. Это позволяет включать код для добавления привязок переменных, которые влияют на существующий код, не изменяя его. Типичным примером является переменная типа * standard-output *. Можно перепривязать переменную, и все выходные данные, использующие эту переменную во время динамической области нового связывания, пойдут в новом направлении. Ричард Столлман утверждал, что для него было очень важно, чтобы в Emacs Lisp было установлено значение по умолчанию (хотя Столлман знал о лексическом связывании в Scheme и Common Lisp).
Lisp имеет эти и другие возможности, потому что он использовался для реализации множества различных языков и парадигм программирования. Типичным примером является встроенная реализация логического языка, скажем, Prolog. Lisp позволяет описывать термины Prolog с помощью s-выражений и с помощью специального компилятора, термины Prolog могут быть скомпилированы в код на Lisp. Иногда требуется обычный синтаксис Prolog, тогда парсер будет анализировать типичные термины Prolog в формы Lisp, которые затем будут компилироваться. Другими примерами встроенных языков являются языки на основе правил, математические выражения, термины SQL, встроенный ассемблер Lisp, HTML, XML и многие другие.