Почему вещи такие, какие они есть?
Первая версия Common Lisp была разработана, начиная с 1981/82 года, а результат был опубликован в виде книги Common Lisp the Language в 1984 году. Сам язык был в значительной степени разработан на основе Lisp Machine Lisp (он же Zetalisp).Zetalisp был намного больше, чем опубликованный тогда Common Lisp, и включал раннюю объектную систему под названием Flavors.Большая часть вещей в Zetalisp была реализована объектно-ориентированным способом - который стоил производительности, а на машинах Lisp производительность была не такой большой - но у них были специализированные процессоры, которые оптимизировали наборы команд.Таким образом, в Common Lisp не было никакой объектной системы, и поэтому он был немного оптимизирован для производительности на типичных процессорах того времени.В более поздней версии Common Lisp должна была быть добавлена объектная система - когда было достаточно опыта работы с объектно-ориентированными расширениями для Lisp - помните, мы говорим о начале 80-х.
Этот Common Lisp 1984 годаимел ограниченную форму общего поведения.Например, функция REMOVE
работает с последовательностями, а последовательности представляют собой новый тип, в котором векторы и списки являются подтипами.
Позднее Common Lisp был стандартизирован, начиная с 1986 года, и каждый искал объектную систему дляCommon Lisp - ни один из предложенных вариантов не был достаточно хорошим - поэтому был разработан новый, основанный на New Flavors (от Symbolics, более новой версии вышеупомянутых Flavors) и Common Loops (от Xerox PARC).У них уже были общие функции, но одна отправка.Затем в CLOS была добавлена множественная диспетчеризация.
Было решено не заменять базовые функции универсальными функциями CLOS - одна из причин этого - производительность: универсальным функциям CLOS требуется относительно сложный механизм диспетчеризации, и эта диспетчеризация определяется во время выполнения.Нет функции CLOS для статической диспетчеризации во время компиляции, а также нет стандартизированной функции для того, чтобы части классов были «запечатаны» ->, чтобы их нельзя было изменить.Таким образом, такая высокодинамичная система, как CLOS, требует затрат времени выполнения.
Некоторые функции определены как универсальные функции CLOS (например, PRINT-OBJECT
), а некоторые реализации содержат большие части Common Lisp с реализациями CLOS (потоки, условия,...) - но это зависит от реализации и не требуется стандартом.Существует также несколько библиотек, которые предоставляют функциональность CLOS для встроенных функций CL: например, ввод-вывод с расширяемыми потоками на основе CLOS.
Также обратите внимание, что переопределение существующих функций Common Lisp является неопределенным поведением.
Таким образом, Common Lisp решил предоставить мощную объектную систему, но оставил за отдельными реализациями, насколько они хотят использовать CLOS на базовом языке - с ограничениями на функции, которые стандартизированы как обычные функции, не относящиеся к CLOS.обычно пользователи не должны заменять их функциями CLOS.
Некоторые диалекты / реализации Lisp пытались решить эти проблемы и пытались определить более быстрый вариант CLOS, который затем стал бы основой для большей части языка.Смотрите, например, язык Дилана от Apple.Для более нового подхода см. Язык Julia.
Ваши собственные улучшенные Common Lisp
Пакеты (-> пространства имен символов) позволяют вам определить свой собственный улучшенный CL: здесь мыопределить новый пакет, в котором есть все символы CL, а только cl:remove
затеняется собственным символом.Затем мы определяем универсальную функцию CLOS с именем bettercl::remove
и пишем два примера методов.
CL-USER 165 > (defpackage "BETTERCL" (:use "CL") (:shadow cl:remove))
#<The BETTERCL package, 1/16 internal, 0/16 external>
CL-USER 166 > (in-package "BETTERCL")
#<The BETTERCL package, 1/16 internal, 0/16 external>
BETTERCL 167 > (defgeneric remove (item whatever))
#<STANDARD-GENERIC-FUNCTION REMOVE 4060000C64>
BETTERCL 168 > (defmethod remove (item (v vector)) (cl:remove item v))
#<STANDARD-METHOD REMOVE NIL (T VECTOR) 40200AB12B>
BETTERCL 169 > (remove 'a #(1 2 3 a b c))
#(1 2 3 B C)
BETTERCL 170 > (defmethod remove ((digit integer) (n integer))
"remove a digit from an integer, returns a new integer"
(let ((s (map 'string
(lambda (item)
(character (princ-to-string item)))
(cl:remove digit
(map 'vector
#'digit-char-p
(princ-to-string n))))))
(if (= (length s) 0) 0 (read-from-string s))))
#<STANDARD-METHOD REMOVE NIL (INTEGER INTEGER) 40200013C3>
BETTERCL 171 > (remove 8 111888111348111)
11111134111
Теперь вы также можете экспортировать символы из BETTERCL
, чтобы вы могли использовать этот пакет в ваших пакетах приложений,вместо пакета CL
.
Этот подход использовался ранее.Например, CLIM (диспетчер интерфейса Common Lisp) определяет пакет CLIM-LISP , который используется в качестве диалекта для программирования.
Common Lisp иногда предоставляет и функцию, иродственная функция CLOS
См. Стандартную функцию DESCRIBE , которую можно расширить с помощью методов написания стандартной универсальной функции CLOS DESCRIBE-OBJECT .
Эксперименты по улучшению дляCommon Lisp
Еще один способ сделать расширяемые стандартные функции Common Lisp - это заменить их версиями, которые затем используют расширяемый протокол на основе CLOS.Обратите внимание, что способ замены стандартных функций зависит от реализации, а эффекты также могут зависеть от реализации.Если, например, компилятор встроил встроенную функцию в код, то переопределение не повлияет на уже встроенный код.
Пример такого подхода см. В документе (PDF) User-расширяемые последовательности в Common Lisp от Christophe Rhodes.
Когда использовать универсальные функции?
Есть несколько вещей, которые следует иметь в виду:
определяют универсальные функции CLOS, когда существует несколько связанных методов , которые в идеале используют общий механизм расширения и которые могут быть расширены
подумайте о снижении производительности
не используйте единую универсальную функцию CLOS для функций, имеющих похожий список аргументов и даже то же имя, но которые работаютдля очень разных доменов
Это означает, что вы не должны определять функции в одной универсальной функции CLOS, например:
; do some astrophysics calculations
(defmethod rotate-around ((s star) (u galaxy)) ...)
; do some computation with graphics objects
(defmethod rotate-around (shape (a axis)) ...)
Например, писать :before
, :around
и :after
методы могут не привести к полезным результатам.
У одного может быть две разные rotate-around
универсальные функции, одна в пакете ASTRO-PHYSICS
и другая в пакете GRAPHICS-OBJECTS
.Таким образом, методы не будут находиться в одной и той же универсальной функции CLOS, и их расширение, вероятно, будет проще.