28.2 Функции для работы с CLOS

Данный раздел описывает функции, макросы, специальные операторы и обобщённые функции для работы с CLOS. Данный интерфейс охватывает функции и макросы, которых достаточно для написания большинства объектно-ориентированных программ.

Описание каждой функции, макроса, специального оператора и обобщённой функции включает предназначение, синтаксис, семантику аргументов, возвращаемые значения и часто примеры и ссылки на связанные функции.

Описание синтаксиса для функции, макроса или специального оператора содержит информацию о их параметрах. Описание обобщённой функции включает описание методов, которые для этой функции определены в CLOS. Сигнатура метода используется для описания параметров и их специализаторов для каждого метода.

Следующий пример показывает формат описания обобщённой функции с сигнатурой для главного метода.

[Обобщённая функция] f x y &optional z &key :k
[Главный метод] f (x class) (y t) &optional z &key :k

Данное описание показывается, что обобщённая функция f имеет два обязательных параметра: x и y. Кроме того присутствуют один необязательный параметр z и именованный параметр :k.

Сигнатура метода показывает, что этот метод для обобщённой функции f имеет два обязательных параметра, x, который должен быть экземпляром класса class, и y, который может быть любым объектом. Кроме того присутствуют один необязательный параметр z и именованный параметр :k. Сигнатура также показывает, что этот метод f является главным и не имеет квалификаторов.

Описание синтаксиса для обобщённой функции описывает лямбда-список самой функции, тогда как сигнатура метода описывает лямбда-списки определённых методов.


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

Любая реализация CLOS может предоставлять дополнительные методы для описанных здесь обобщённых функций.

Функции и макросы можно распределить по категориям так:

[At this point the original CLOS report contained a description of the [[ ]] and notation; that description is omitted here. I have adopted the notation for use throughout this book. It is described in section 1.2.5.—GLS]

[Обобщённая функция] add-method generic-function method
[Главный метод] add-method (generic-function standard-generic-function) (method method)

Обобщённая функция add-method добавляет метод в обобщённую функцию. Она деструктивно модифицирует обобщённую функцию и возвращает её в качестве результата.

Аргумент generic-function является объектом обобщённой функции.

Аргумент method является объектом метода. Лямбда-список функции метода должен быть согласован с лямбда-списком обобщённой функции, иначе будет сигнализирована ошибка.

Функция возвращает модифицированный объект обобщённой функции. Результата функции add-method равен eq аргументу generic-function.

Если заданный метод совпадает с уже существующим в данной обобщённой функции методом в специализаторах параметров и квалификаторах, то он заменяется на новый. Смотрите раздел 28.1.6 для подробного описания механизма.

Если объект метода принадлежит другой обобщённой функции, сигнализируется ошибка.

Смотрите раздел 28.1.6.

Смотрите defmethod, defgeneric, find-method и remove-method.


[Макрос] call-method method next-method-list

Макрос call-method используется в сочетании методов. Данный макрос скрывает платформоспецифичные штучки о том, как происходят вызовы методов. Он может быть использован только внутри формы метода, так как имя call-method определено только в лексической области данной формы.

Макрос call-method вызывает указанный метод, передавая ему аргументы и определения для call-next-method и для next-method-p. Аргументы те же, что и переданные в метод содержащий вызов данного макроса. Определения call-next-method и next-method-p опираются на список объектов методов, переданный в качестве второго аргумента call-method.

Функция call-next-method, доступная для метода из первой подформы, вызовет первый метод из списка из второй подформы. Функция call-next-method, доступная в этом методе, в свою очередь, вызовет второй метод в списке из второй подформы, и так далее, пока список методов не закончится.

Аргумент method является объектом метода. Аргумент next-method-list является списком объектов методов.

Список, у которого первый элемент является символом make-method, и второй элемент которого является Lisp’овой формой, может использоваться вместо объекта метода на месте первой подформы макроса call-method или в качестве элемента списка во второй подформе. Такой список (make-method lisp-form) задаёт объект метода, тело которого состоит из формы lisp-form.

Результатом call-method является значение(я), возвращённые вызываемым методом.

Смотрите call-next-method, define-method-combination и next-method-p.


[Функция] call-next-method &rest args

Функция call-next-method может использоваться внутри тела метода для вызова следующего метода.

Функция call-next-method возвращает значение(я), которые были возвращены вызванным методом. Если следующего метода не оказалось, вызывается обобщённая функция no-next-method.

Для определения того, какие методы могут вызывать call-next-method используется тип сочетания методов. Стандартный тип сочетания методов позволяет использовать call-next-method в главных и :around методах.

Стандартный тип сочетания методов находит следующий метод в соответствие с правилами:

Часть описания call-next-method содержится в разделах 28.1.7 и 28.1.7.

Когда call-next-method вызывается без аргументов, она передаёт аргументы для текущего метода в следующий метод. Ни значения аргументов по-умолчанию, ни использование setq, ни пересвязывания переменных с теми же именами, что и параметры текущего метода не влияют на значения, передаваемые функцией call-next-method в следующий метод.

Если call-next-method вызывается с аргументами, они передаются при вызове в следующий метод. При указании аргументов для call-next-method должно выполняться следующее правило, иначе будет сигнализирована ошибка: Упорядоченное множество методов, которые применимы к аргументам указанным в call-next-method должно быть эквивалентно множеству аргументов, которые применимы к оригинальным аргументам, указанным в текущий метод. Допускаются оптимизации проверки данной ошибки, но они не должны изменять семантики call-next-method.

Если call-next-method вызывается с аргументами, но необязательные параметры не указываются, следующий метод будет вызван со значениями по-умолчанию для данных аргументов.

Функция call-next-method возвращает значение(я), которые были возвращены следующим методом.

После того, как call-next-method возвращает управление, можно проводить дальнейшие вычисления.

Определение функции call-next-method имеет лексическую область видимости (она определена только внутри тела метода) и неограниченную продолжительность видимости.

Для обобщённых функций, которые используют сочетания методов, определённые с помощью формы define-method-combination, call-next-method может использоваться только в :around методах.

Для проверки, существует ли следующий метод, может использоваться функция next-method-p.

Если call-next-method используется в методах, которые её не поддерживают, сигнализируется ошибка.

Смотрите разделы 28.1.7, 28.1.7 и 28.1.7. Смотрите функции define-method-combination, next-method-p и no-next-method.


[Обобщённая функция] change-class instance new-class
[Главный метод] change-class (instance standard-object) (new-class standard-class)
[Главный метод] change-class (instance t) (new-class symbol)

Обобщённая функция change-class изменяет класс экземпляра instance на новый класс new-class. Функция деструктивно модифицирует и возвращает экземпляр.

Если в старом классе были какие-либо слоты с теми же именами, что и локальные слоты в новом классе, тогда значения таких слотов остаются прежними. Это значит, что если в слоте было значение, тогда значение, возвращаемое slot-value после вызова change-class, будет равно eql значение, возвращаемому slot-value перед вызовом change-class. Если слот не имел связывания, то после изменения класса он также не будет иметь связывания. Другие слоты будут инициализированы так, как описано в разделе 28.1.11.

Аргумент instance является Lisp’овым объектом.

Аргумент new-class является объектом класса или символом, указывающим на класс.

Если применяется второй из перечисленных выше методов, то этот метод вызывает change-class для экземпляра instance и (find-class new-class).

В качестве результата возвращается модифицированный экземпляр. Результат change-class равен eq аргументу instance.

Например:

(defclass position () ())

(defclass x-y-position (position)
  ((x :initform 0 :initarg :x)
   (y :initform 0 :initarg :y)))

(defclass rho-theta-position (position)
  ((rho :initform 0)
   (theta :initform 0)))

(defmethod update-instance-for-different-class :before
           ((old x-y-position)
            (new rho-theta-position)
            &key)
  ;; Copy the position information from old to new to make new
  ;; be a rho-theta-position at the same position as old.
  (let ((x (slot-value old ’x))
        (y (slot-value old ’y)))
    (setf (slot-value new ’rho) (sqrt (+ (* x x) (* y y)))
          (slot-value new ’theta) (atan y x))))

;;; С помощью change-class экземпляр класса x-y-position
;;; может быть изменён так, чтобы классом
;;; стал rho-theta-position

(setq p1 (make-instance ’x-y-position :x 2 :y 0))

(change-class p1 ’rho-theta-position)

;;; Результатом является то, что экземпляр связанный
;;; с символом p1 сейчас является экземпляром класса
;;; rha-theta-position.
;;; Метод update-instance-for-different-class выполнил
;;; инициализацию слотов rho и theta, основываясь ;;; на значениях слотов x и y, которые были в старом ;;; экземпляре.

После завершения всех других операций, change-class вызывает обобщённую функцию update-instance-for-different-class. Обобщённая функция update-instance-for-different-class может быть использована для присваивания значений слотам в трансформированном экземпляре класса.

Обобщённая функция change-class имеет несколько семантических сложностей. Первое, она выполняет деструктивную операцию, которая может быть вызвана внутри метода для экземпляра, который использовался для выбора применяемого метода. Если была произведено сочетание методов, то следующие методы могут оказаться неприменимыми. Второе, некоторые реализации могут использовать оптимизацию при компилировании в доступах к слотам, и когда класс экземпляра изменяется, то этот код «может поломаться». Вывод такое, что программист не должен использовать change-class внутри методов, если любой метод для текущей обобщённой функции оперирует слотами этого экземпляра. В противном случае результаты непредсказуемы.

Смотрите раздел 28.1.11.

Смотрите update-instance-for-different-class.


[Обобщённая функция] class-name class
[Главный метод] class-name (class class)

Обобщённая функция class-name принимает объект класса и возвращает его имя.

Аргумент class является объектом класса.

Функция возвращает имя заданного класса.

Именем для анонимного класса является символ nil.

Если S является таким символом, что S =(class-name C) и C =(find-class S), тогда S является именем собственным для C. Смотрите раздел 28.1.2.


[Обобщённая функция] (setf class-name)  new-value class
[Главный метод] (setf class-name)  new-value (class class)

Обобщённая функция (setf class-name) принимает объект класса и устанавливает его имя. Аргумент class является объектом класса. Аргумент new-value является любым объектом.


[Функция] class-of object

Функция class-of возвращает класс, к которому принадлежит экземпляр класса object. Аргументом для class-of может быть любой Common Lisp’овый объект.


[Функция] compute-applicable-methods generic-function function-arguments

Функция compute-applicable-methods принимает обобщённую функцию и аргументы для неё и возвращает множество методов, которые применимы для данных аргументов.

Методы сортируются в соответствии с precedence order. Смотрите раздел 28.1.7.

Аргумент generic-function должен быть объектом обобщённой функции. Аргумент function-arguments является списком аргументов для обобщённой функции. Результатом является списком применимых методов в порядке приоритетности. Смотрите раздел 28.1.7.


[Макрос] defclass class-name ({superclass-name}*)({slot-specifier}*) [[class-option]]

class-name ::= symbol
superclass-name ::= symbol
slot-specifier ::= slot-name | (slot-name [[↓slot − option ]])
slot-name ::= symbol
slot-option ::= {:reader reader-function-name}*
|  {:writer writer-function-name}*
|  {:accessor reader-function-name}*
|  {:allocation allocation-type}
|  {:initarg initarg-name}*
|  {:initform form}
|  {:type type-specifier}
|  {:documentation string}

reader-function-name ::= symbol
writer-function-name ::= function-name
function-name ::= {symbol | (setf symbol)}
initarg-name ::= symbol
allocation-type ::= :instance | :class
class-option ::= (:default-initargs initarg-list)
(:documentation string)
(:metaclass class-name)
initarg-list ::= {initarg-name default-initial-value-form}*

Макрос defclass определяет именованный класс. В качестве результата он возвращает объект нового класса.

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

Определение нового класса также создаёт определение типа с таким же именем. Предикат (typep object class-name) возвращает истину, если класс данного объекта object является классом или подклассом с именем class-name. Объект класса может использоваться в качестве спецификаторы типа. Таким образом (typep object class) возвращает истину, если object является классом или подклассом class.

Аргумент class-name является не-nil символом. Он становится для класса именем собственным. Если класс с таким же именем собственным уже существует, и этот класс является экземпляром класса standard-class, и если форма defclass определяет класс от класса standard-class, определение нового класса заменяет старый.

Каждый аргумент superclass-name является не-nil символом, который указывает прямые суперклассы для нового класса. Новый класс будет наследовать слоты и методы от каждого их этих суперклассов, от их суперклассов и так далее. Смотрите раздел 28.1.3 для описания наследования слотов и методов.

Каждый аргумент slot-specifier является или просто именем слота, или списком, который содержит имя слота и ноль и более его параметров. Аргумент slot-name является символом, который синтаксически корректен, то есть мог бы использоваться для имён переменных. Если один и тот же слот был указан дважды, будет сигнализирована ошибка.

В описании слота можно использовать следующие параметры:

Каждый параметр класса относится к классу в целом или ко всем слотам. Доступны следующие параметры:

В качестве результата возвращается созданный объект класса.

Если класс с таким же именем собственным уже существует, и этот класс является экземпляром standard-class и если форма defclass с определением нового класса также использует standard-class, существующий класс переопределяется, и экземпляры данного класса (и всех подклассов) обновляются до нового определения класса во время следующего к ним обращения. Смотрите раздел 28.1.10.

Для стандартных классов применяются следующие правила:

Объектная система может быть расширены для обработки ситуаций не описанные данными правилами.

Некоторые параметры слота наследуются от суперклассов, и некоторые из них могут быть затенены или изменены в текущем описании слота. Параметры класса, кроме :default-initargs, не наследуются. Для подробного описания того, как наследуются слоты и их параметры, смотрите раздел 28.1.3.

Реализация может добавлять свои параметры формы defclass. Однако если реализация сталкивается с неподдерживаемым параметром, они должна сигнализировать ошибку.

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

Если для слота не определены ни функции чтения, записи, ни аксессор, тогда к нему можно получить доступ только с помощью функции slot-value.

Смотрите разделы 28.1.2, 28.1.3, 28.1.10, 28.1.5, 28.1.9.

Смотрите slot-value, make-instance и initialize-instance.


[Макрос] defgeneric function-name lambda-list[[option | {method-description}*]]

function-name ::= {symbol | (setf symbol)}
lambda-list ::= ( {var}*
[&optional {var | (var)}*]
[&rest var]
[&key {keyword-parameter}* [&allow-other-keys]] )

keyword-parameter ::= var | ( {var | (keyword var)} )
option ::= (:argument-precedence-order {parameter-name}+ )
(declare {declaration}+ )
(:documentation string)
(:method-combination symbol {arg}* )
(:generic-function-class class-name)
(:method-class class-name)
method-description ::= (:method {method-qualifier}*
specialized-lambda-list
[[ {declaration}* | documentation]]
{form}* )
method-qualifier ::= non-nil-atom
specialized-lambda-list ::=
( {var | (var parameter-specializer-name)}*
[&optional {var | (var [initform [supplied-p-parameter]])}*]
[&rest var]
[&key {specialized-keyword-parameter}* [&allow-other-keys]]
[&aux {var | (var [initform])}*] )
specialized-keyword-parameter ::=
var | ( {var | (keyword var)} [initform [supplied-p-parameter]] )
parameter-specializer-name ::= symbol | (eql eql-specializer-form)

Макрос defgeneric используется для определения функции или для указания параметров и деклараций, которые относятся к обобщённой функции в целом.

Если (fboundp function-name) является nil, тогда создаётся новая обобщённая функция. Если (fdefinition function-specier) является обобщённой функцией, тогда она модифицируется. Если function-name задаёт необобщённую функцию, макрос или специальный оператор, то сигнализируется ошибка.

Каждый method-description определяет метод для обобщённой функции. Лямбда-список каждого метода должен соответствовать лямбда-списку, заданному в параметре lambda-list. Если это условие не выполняется, сигнализируется ошибка. Смотрите раздел 28.1.6 для определения соответствия лямбда-списков в данном контексте.

Макрос defgeneric в качестве результата возвращает объект обобщённой функции.

Аргумент function-name является не-nil символом или списком вида (setf symbol).

Аргумент lambda-list является обычным лямбда-списком со следующими ограничениями:

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

Аргументы method-description определяет методы, которые будут связаны с данной обобщённой функцией. Аргументы method-qualifier и specialized-lambda-list в описании метода значат то же, что и в defmethod.

Аргументы form определяют тело метода. Тело метода заключается в неявный блок. Если function-name является символом, данный блок будет с тем же именем, что и обобщённая функция. Если function-name является списком формы (setf symbol), имя блока будет совпадать с symbol.

Результатом является объект обобщённой функции.

Следствием макроса defgeneric является выполнение следующих трёх шагов: первое, методы определённые с помощью предыдущей defgeneric удаляются; второе, вызывается ensure-generic-function; и третье, методы, определённые в данной форме defgeneric добавляются в обобщённую функцию.

Если ни одно описание метода не было указано и обобщённая функция до этого не существовала, создаётся обобщённая функция без методов.

Аргумент lambda-list формы defgeneric указывает форму лямбда-списков для методов данной обобщённой функции. Все её методы должны иметь лямбда-списки согласованные с данной формой. Если форма defgeneric вычисляется и некоторые методы для этой обобщённой функции содержат не согласующиеся с этим аргументом лямбда-списки, сигнализируется ошибка. Для подробностей смотрите раздел 28.1.6.

Реализации могут расширять defgeneric дополнительными параметрами. Однако реализация должна сигнализировать ошибку при столкновении с неподдерживаемым параметром.

Смотрите раздел 28.1.6.

Смотрите defmethod, ensure-generic-function и generic-function.


[Макрос] define-method-combination name [[short-form-option]]

[Макрос] define-method-combination name lambda-list({method-group-specifier}*)[(:arguments . lambda-list)][(:generic-function generic-fn-symbol)][[{declaration}* | doc-string]]{form}*

short-form-option ::= :documentation string
:identity-with-one-argument boolean
:operator operator
method-group-specifier ::= (variable { {qualifier-pattern}+ | predicate}
[[long-form-option]] )
long-form-option ::= :description format-string
:order order
:required boolean

Макрос define-method-combination используется для определения новых типов сочетаний методов.

Макрос define-method-combination имеет две формы. Краткая форма предоставляет упрощённую функциональность для частого и быстрого использования. Длинная форма более мощная, но и более подробная. Макрос схож с defmacro в том, что тело является выражением, обычно с использованием обратной кавычки, которое вычисляется в Lisp’овую форму. Таким образом, реализованы могут быть любые управляющие конструкции. Длинная форма также позволяет произвольную обработку квалификаторов методов.

В обеих формах name является символом. По соглашению, оно не может быть ключевым символом или nil.

Если вторая подформа является не-nil символом или не представлена вовсе, используется синтаксис краткой формы define-method-combination. Когда используется краткая форме, name определяется как тип сочетания методов, который создаёт Lisp’овую форму (operator method-call method-call ). operator является символом, которые может быть именем функции, макроса или специального оператора. operator может быть указан ключевым параметров, по-умолчанию он равен name.

Ключевые параметры для краткой формы:

Ни одна из подформ не вычисляется.

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

Определённая таким образом процедура сочетания методов для методов определяет две роли. Метод, у которого единственный квалификатор является таким же символом, что и тип сочетания, считается главным. Как минимум один главный метод должен быть применимым, иначе сигнализируется ошибка. Метод с единственным квалификатором :around является вспомогательным, и его поведение совпадает с такими же методами в стандартном типе сочетания методов. Функция call-next-method может быть вызвана только в :around методах. Она не может быть использована в главных методах, определённых краткой формой макроса define-method-combination.

Определённая таким образом процедура сочетания методов, принимает необязательный аргумент с именем order, который по-умолчанию равен :most-specific-first. Значение :most-specific-last изменяет порядок главный метод на обратный, порядок же вспомогательных методов остаётся прежним.

Краткая форма автоматически включает проверку ошибок и поддержку для :around методов.

Описание встроенных типов сочетания методов смотрите в разделе 28.1.7.

Если вторая подформа является списком, используется синтаксис длинной формы define-method-combination.

Аргумент lambda-list является обычным лямбда-списком. Он принимает любые аргументы, указанные после имени сочетания методов в параметре :method-combination в форме defgeneric.

Далее следует список спецификаторов группирования методов. Каждый спецификатор или с помощью сравнения квалификаторов с некоторыми шаблонами, или проверкой квалификаторов с помощью предиката выделяет подмножество применимых для некоторой роли методов. Эти спецификаторы группирования методов определяют все квалификаторы, которые могут использоваться для данного типа сочетания методов. Если применимый метод не попадает ни в одну из групп, система сигнализирует ошибку о том, что метод некорректен для использования с этим типом сочетания.

Каждый спецификатор группирования методов указывает на переменную. В процессе выполнения форм в теле define-method-combination, эта переменная связывается со списком методов в данной группе. Методы в данном списке отсортированы от наиболее специфичного к наименее.

Квалификационный шаблон является списком или символом *. Метод подходит под шаблон, если список квалификаторов метода равен equal шаблону (за исключением того, что символ * указывает на любой квалификатор). Таким образом квалификационный шаблон может быть одним из следующих: пустой список (), который указывает на неквалифицированный метод; символ *, который указывает на все методы; Ъ список, который указывает на метод с таким же количеством таких же что и в нём квалификаторов или список с точкой, который заканчивается символом * (* указывает на любое количество дополнительный квалификаторов).

Каждый применимый метод проверяется на соотвествие квалификационным шаблонам и предикатам в порядке слева направо. Как только метод удовлетворяет квалификационному шаблону или предикату, метод становится элементом соответствующей группировки методов и больше ни на что не проверяется. Таким образом, если метод может быть элементом более одной группы, он включается только в первую группу. Если группировка методов содержит более одного квалификационного шаблона, метод должен удовлетворить только одному шаблону.

В спецификаторе группирования методов вместо квалификационных шаблонов может быть указано имя предиката. Предикат вызывается для каждого метода, который ещё не входит ни в одну группу. Он вызывается с одним аргументом: списком квалификаторов метода. Предикат должен вернуть истину, если метод принадлежит некоторой группе методов. Предикат может быть отличён от квалификационного шаблона, потому что это символ не может быть nil или *.

Если для данного типа сочетания методов у применимых методов квалификаторы не являются корректными, вызывается функция invalid-method-error.

Спецификаторы группирования методов после квалификационных шаблонов или предиката могут содержать ключевые параметры. Ключевые параметры могут быть отличены от дополнительных квалификационных шаблонов, потому что не являются списками или символом *. Ключевые параметры:

Использование спецификаторов группирования методов предоставляет удобный синтаксис для выбора методов, разделения их на возможные роли, и выполнения необходимой проверки ошибок. Возможно выполнение дальнейшей фильтрации методов в теле с помощью использования обычных операций обработки списком и функций method-qualifiers и invalid-method-error. Возможно использования setq для переменных, указанных в спецификаторах группирования методов и связывание дополнительных переменных. Также возможно выполнение любых форм. Можно использовать только одну группу методов с шаблоном *. В этом случае переменная будет связана со списком всех применимых методов в порядке от наиболее специфичного к наименее.

Тело из форм forms выполняет вычисления и возвращает Lisp’овую форму, которая определяет то, как сочетаются методы, то есть возвращает рабочий метод. Рабочий метод используется макрос call-method. Определение этого макроса имеет лексическую область видимости и доступно только внутри рабочего метода. Учитывая метод объекта в одном из списков, полученных с помощью спецификатора группирования методов и список следующих методов, макрос call-method будет вызывать метод так, что для вызова следующего метода будет доступна call-next-method.

Когда рабочий метод не имеет никакого эффекта, кроме того как вызвать один метод, некоторые реализации используют оптимизацию, которая использует единственный метод непосредственно в качестве рабочего метода, что позволяет избежать необходимости создать новый рабочий метод. Эта оптимизация является активным, если форма рабочего метода состоит полностью из вызова макроса call-method, первая подформа которого является объектом метода и вторая является подчинённой nil. Если требуется оптимизация каждое тело define-method-combination несёт ответственность за удаление избыточных вызовов progn, and, multiple-value-prog1, и тому подобных.

Список (:arguments . lambda-list) может быть указан перед любой декларацией или строкой документации. Эта форма полезна, когда тип сочетания методов выполняет некоторое особенное поведение как часть объединённого метода, и это поведение требует доступа к аргументам обобщённой функции. Каждая переменная параметра, определённый в lambda-list, связывается с формой, которая может быть вставлена в рабочий метод. Когда эта форма вычисляется в процессе выполнения рабочего метода, её значение является соответствующим аргументом к обобщённой функции. Если lambda-list не согласован с лямбда-списком обобщённой функции, для согласования будут вставлены дополнительные игнорируемые параметры. Таким образом, lambda-list может получать меньше аргументов, чем ожидает обобщённая функция.

Ошибочные условия, обнаруженные в теле, должны быть опубликованы с помощью method-combination-error или method-combination-error. Эти функции добавляют любую необходимую контекстную информацию с сообщением об ошибке и сигнализируют соответствующую ошибку.

Тело forms вычисляется внутри связываний, созданных лямбда-списком и спецификаторами группирования методов. Декларации в начале тела помещаются напрямую внутрь связывании, созданных лямбда-списком, и вне связываний переменных группирования методов. Таким образом переменные группирования методов не могут быть задекларированы.

Внутри тела form, generic-function-symbol связан с объектом обобщённой функции.

Если указан аргумент doc-string, он содержит строку документации для типа сочетания методов.

Из тела forms или из функций, вызванных в этом теле, могут быть вызваны функции method-combination-error и invalid-method-error. Действия этих двух функций зависят от динамических переменных, которые были автоматически связаны перед вызовом обобщённой функции compute-effective-method.

Результатом вычисления макроса define-method-combination является новый объект сочетания методов.

Большинство примеров использования длинной формы define-method-combination также иллюстрируют использования связанных функций, которые предоставлены как часть функционала декларативного сочетания методов.

;;; Примеры краткой формы define-method-combination

(define-method-combination and :identity-with-one-argument t)

(defmethod func and ((x class1) y)
  ...)

;;; Эквивалент этого примера в длинной форме:

(define-method-combination and
        (&optional (order ’:most-specific-first))
        ((around (:around))
         (primary (and) :order order :required t))
  (let ((form (if (rest primary)
                  ‘(and ,@(mapcar #’(lambda (method)
                                      ‘(call-method ,method ()))
                                  primary))
                  ‘(call-method ,(first primary) ()))))
    (if around
        ‘(call-method ,(first around)
                      (,@(rest around)
                       (make-method form)))
        form)))


;;; Пример длинной формы define-method-combination

;;; Способ сочетания методов по-умолчанию

(define-method-combination standard ()
        ((around (:around))
         (before (:before))
         (primary () :required t)
         (after (:after)))
  (flet ((call-methods (methods)
           (mapcar #’(lambda (method)
                       ‘(call-method ,method ()))
                   methods)))
    (let ((form (if (or before after (rest primary))
                    ‘(multiple-value-prog1
                       (progn ,@(call-methods before)
                              (call-method ,(first primary)
                                           ,(rest primary)))
                       ,@(call-methods (reverse after)))
                    ‘(call-method ,(first primary) ()))))
      (if around
          ‘(call-method ,(first around)
                        (,@(rest around)
                         (make-method form)))
          form))))

;;; Простой путь вызова нескольких методов, пока не вернётся не-nil

(define-method-combination or ()
        ((methods (or)))
  ‘(or ,@(mapcar #’(lambda (method)
                     ‘(call-method ,method ()))
                 methods)))

;;; Более полная версия предыдущего примера

(define-method-combination or
        (&optional (order ’:most-specific-first))
        ((around (:around))
         (primary (or)))
  ;; Process the order argument
  (case order
    (:most-specific-first)
    (:most-specific-last (setq primary (reverse primary)))
    (otherwise (method-combination-error
                 "~S is an invalid order.~@
                  :most-specific-first and :most-specific-last ~
                    are the possible values."
                                         order)))
  ;; Must have a primary method
  (unless primary
    (method-combination-error "A primary method is required."))
  ;; Construct the form that calls the primary methods
  (let ((form (if (rest primary)
                  ‘(or ,@(mapcar #’(lambda (method)
                                     ‘(call-method ,method ()))
                                 primary))
                  ‘(call-method ,(first primary) ()))))
    ;; Wrap the around methods around that form
    (if around
        ‘(call-method ,(first around)
                      (,@(rest around)
                       (make-method form)))
        form)))

;;; То же, с использованием ключевых параметров :order и :required
(define-method-combination or
        (&optional (order ’:most-specific-first))
        ((around (:around))
         (primary (or) :order order :required t))
  (let ((form (if (rest primary)
                  ‘(or ,@(mapcar #’(lambda (method)
                                     ‘(call-method ,method ()))
                                 primary))
                  ‘(call-method ,(first primary) ()))))
    (if around
        ‘(call-method ,(first around)
                      (,@(rest around)
                       (make-method form)))
        form)))

;;; This short-form call is behaviorally identical to the preceding.
(define-method-combination or :identity-with-one-argument t)

;;; Сортировка методов по положительному целому квалификатору; :around
;;; метода для краткости примера отключены.

(define-method-combination example-method-combination ()
        ((methods positive-integer-qualifier-p))
  ‘(progn ,@(mapcar #’(lambda (method)
                        ‘(call-method ,method ()))
                    (stable-sort methods #’<
                      :key #’(lambda (method)
                               (first (method-qualifiers
                                        method)))))))

(defun positive-integer-qualifier-p (method-qualifiers)
  (and (= (length method-qualifiers) 1)
       (typep (first method-qualifiers) ’(integer 0 *))))

;;; Пример использования :arguments
(define-method-combination progn-with-lock ()
        ((methods ()))
        (:arguments object)
  ‘(unwind-protect
       (progn (lock (object-lock ,object))
              ,@(mapcar #’(lambda (method)
                            ‘(call-method ,method ()))
                        methods))
     (unlock (object-lock ,object))))

Параметр :method-combination defgeneric используется для указания того, что обобщённая функция должна использовать специальный тип сочетания методов. Аргумент в параметре :method-combination является именем этого типа сочетания методов.

Смотрите разделы 28.1.7 и 28.1.7.

Смотрите call-method, method-qualifiers, method-combination-error, invalid-method-error и defgeneric.


[Макрос] defmethod function-name {method-qualifier}*specialized-lambda-list[[{declaration}* | doc-string]] {form}*

function-name ::= {symbol | (setf symbol)}
method-qualifier ::= non-nil-atom
parameter-specializer-name ::= symbol | (eql eql-specializer-form)

Макрос defmethod определяет метод для обобщённой функции.

Если форма (fboundp function-name) равна nil, создаётся обобщённая функция со значениями по-умолчанию: для списка приоритетности аргументов (каждый аргумент более специфический чем те, которые в списке стоят справа от него), для класса обобщённой функции (класс standard-generic-function), для класса метод (класс standard-class) и для типа сочетания методов (стандартный тип сочетания методов). Лямбда-список обобщённой функция будет согласован с лямбда-списком определяемого метода. Если форма defmethod содержит именованные параметры, лямбда-список обобщённой функции будет содержать &key (но не сами именованные параметры). Если function-name указывает на необобщённую функцию, макрос или специальный оператор, сигнализируется ошибка.

Если обобщённая функция function-name уже существует (где function-name является символом или списком типа (setf symbol)), лямбда-список метода должен быть согласован с её лямбда-списком. Если это условие не выполняется, сигнализируется ошибка. Для определения согласованности смотрите раздел 28.1.6.

Аргумент function-name является не-nil символом или списком формы (setf symbol). Он указывает на обобщённую функцию, для которой определяется метод.

Каждый аргумент method-qualifier является объектом, который используется объектом сочетания методов для определения данного метода. Квалификатор метода является не-nil атомом. Тип сочетания методов может ограничивать допустимые значения квалификаторов. Стандартный тип сочетания методов допускает неквалифицированные методы или методы, у которых назначено исключительно :before, :after или :around.

specialize-lambda-list является обычным лямбда-списком за исключением того, что имена обязательных параметров могут быть заменены специализированными параметрами, а, именно, списком вида (variable-name parameter-specializer-name). Специализированы могут быть только обязательные параметры. Имя специализатора параметра является символом, указывающим на класс или (eql eql-specializer-form). Специализатор параметра вида (eql eql-specializer-form) указывает на то, что соответствующий аргумент должен быть равен eql объекту, который является значением eql-specializer-form для применяемого метода. Если для некоторого обязательного параметра не указан специализатор, то в предполагается список вида (variable-name t). Смотрите раздел 28.1.6.

Аргументы form задают тело метода. Тело метода заключается в неявный блок. Если function-name является символом, этот блок будет иметь такое же имя, как и обобщённая функция. Если function-name является списком вида (setf symbol), имя блока будет равно symbol.

Результатом defmethod является объект метода.

Классом объекта метода, будет является класс, который указан в параметре обобщённой функции, для которой создаётся данный метод.

Если обобщённая функция уже содержит метод, который согласуется с текущим в специализаторах параметров и квалификаторах, defmethod заменяет существующий метод новым. Для термина «согласование» в данном контексте смотрите раздел 28.1.6.

Специализаторы параметров описаны в разделе 28.1.6.

Раскрытие макроса defmethod ссылается на каждый специализированный параметр (смотрите спецификатор декларации ignore), включая параметры, которые содержат явное имя специализатора t. Это означает, что предупреждений компилятора не будет, если тело метода не ссылается на специализированный параметр. Следует отметить, что параметр, который специализирован классом t, в этом контексте не является синонимом для неспециализированного параметра (который неявно специализируется к классу t).

Смотрите разделы 28.1.6, 28.1.6 и 28.1.6.


[At this point the original CLOS report [57] contained a specification for describe as a generic function. This specification is omitted here because X3J13 voted in March 1989 not to make describe a generic function after all (see describe-object).—GLS]

[Обобщённая функция] documentation x &optional doc-type
[Главный метод] documentation (method standard-method) &optional doc-type
[Главный метод] documentation (generic-function standard-generic-function) &optional doc-type
[Главный метод] documentation (class standard-class) &optional doc-type
[Главный метод] documentation (method-combination method-combination) &optional doc-type
[Главный метод] documentation (slot-description standard-slot-description) &optional doc-type
[Главный метод] documentation (symbol symbol) &optional doc-type
[Главный метод] documentation (list list) &optional doc-type

Обычная функция documentation (смотрите раздел ??) была заменена обобщённой функцией. Обобщённая функция documentation возвращает строку документации, связанную с данным объектом. Если строка документации не доступна возвращает nil.

Первый аргумент функции может быть символом, именем функции в форме (setf symbol), объектом метода, объектом класса, объектом обобщённой функции, объектом сочетания методов или объектом описания слота. Необходимо ли указывать второй аргумент зависит от типа первого аргумента.

Реализация может дополнять множество символов, использующихся во втором аргументе. Если реализация не поддерживает символ, переданный во втором аргументе, должна быть сигнализирована ошибка.

Функция возвращает связанную строку документации. Если такой строки нет, возвращается nil.


[Обобщённая функция] (setf documentation)  new-value x &optional doc-type
[Главный метод] (setf documentation)  new-value (method standard-method) &optional doc-type
[Главный метод] (setf documentation)  new-value (generic-function standard-generic-function) &optional doc-type
[Главный метод] (setf documentation)  new-value (class standard-class) &optional doc-type
[Главный метод] (setf documentation)  new-value (method-combination method-combination) &optional doc-type
[Главный метод] (setf documentation)  new-value (slot-description standard-slot-description) &optional doc-type
[Главный метод] (setf documentation)  new-value (symbol symbol) &optional doc-type
[Главный метод] (setf documentation)  new-value (list list) &optional doc-type

Обобщённая функция (setf documentation) используется для установки строк документации.

Первый аргумент (setf documentation) новая строка документации.

Второй аргумент documentation может быть символом, именем функции в виде (setf symbol), объектом метода, объектом класса, объектом обобщённой функции, объектом сочетания методов, объектом описания слота. Должен ли третий необязательный аргумент быть указан зависит от типа второго аргумента.

Смотрите documentation.


[Функция] ensure-generic-function function-name &key :lambda-list :argument-precedence-order :declare :documentation :generic-function-class :method-combination :method-class :environment

function-name ::= {symbol | (setf symbol)}

Функция ensure-generic-function используется для определения глобальной обобщённой функции без методов или для изменения параметров и деклараций, которые применяются для глобальной обобщённой функции в целом.

Если (fboundp function-name) равна nil, создаётся новая обобщённая функция. Если (fdefinition function-name) является необобщённой функцией, макросом или специальным оператором, сигнализируется ошибка.

[X3J13 voted in March 1989 to use fdefinition in the previous paragraph, as shown, rather than symbol-function, as it appeared in the original report on CLOS [57]. The vote also changed all occurrences of function-specifier in the original report to function-name; this change is reflected here.—GLS]

Если function-name указывает на обобщённую функцию и значения: :argument-precedence-order, :declare, :documentation, :method-combination, отличаются от старых значений функции, тогда она модифицируется.

Если function-name указывает на обобщённую функцию и значение :lambda-list отличается от старого, но при этом согласуется со лямбда-списками всех методов (или если методы отсутствуют), тогда устанавливается новое значение, иначе сигнализируется ошибка.

Если function-name указывает на обобщённую функцию и значение :generic-function-class отличается от старого и если новая обобщённая функция совместима со старой, тогда вызывается change-class для изменения её класса, иначе сигнализируется ошибка.

Если function-name указывает на обобщённую функцию и значение :method-class отличается от старого, значение изменяется, но классы существующих методов не изменяются.

Аргумент function-name является символом или списком вида (setf symbol).

Именованные параметры соответствуют аргументам option defgeneric, за исключением того, что аргумента :method-class и :generic-function-class могут быть как объектами класса, так и его именами.

Аргумент :environment такой же как аргумент &environment для функции раскрытия макросов. Обычно он используется для различия между временем компиляции и временем выполнения.

Аргумент :method-combination является объектом сочетания методов.

В качестве результата возвращает объект обобщённой функции.

Смотрите defgeneric.


[Функция] find-class symbol &optional errorp environment

Функция find-class возвращает объект класса, на который указывает заданный символ в заданном окружении.

Первый аргумент функции является символом.

Если объект класса не найден и аргумент errorp не задан или равен не-nil значению, find-class сигнализирует ошибку. Если объект класса не найден и аргумент errorp равен nil, find-class возвращает nil. По-умолчанию аргумент errorp равен t.

Результатом find-class является объект класса, который связан с заданным символом.

Связь объекта класса и символа может быть изменена с помощью setf в связке с find-class. Если пользователь пытается изменить класс ассоциированный с символом, который является спецификатором типа из главы 4, то результат не определён. Смотрите раздел 28.1.4.


[Обобщённая функция] find-method generic-function method-qualifiers specializers &optional errorp
[Главный метод] find-method (generic-function standard-generic-function) method-qualifiers specializers &optional errorp

Обобщённая функция find-method принимает обобщённую функцию и возвращает объект метода, который согласуется с квалификаторами method-qualifiers и специализаторами параметров specializers. Смотрите раздел 28.1.6 для определения в данном контексте понятия согласования.

Аргумент generic-function должен быть обобщённой функцией.

Аргумент method-qualifiers является списком квалификаторов метода. Порядок квалификаторов важен.

Аргумент specializers является списком специализаторов параметров. Он должен содержать одинаковое количество обязательных аргументов, что и обобщённая функция, иначе будет сигнализирована ошибка. Это означает, что для получения метода по-умолчанию для данной обобщённой функции, должен быть указан список, элементы которого являются классами с именем t.

Результатом find-method является объект метода, у которого заданные квалификаторы и специализаторы параметров.

Смотрите раздел 28.1.6.


[Обобщённая функция] function-keywords method
[Главный метод] function-keywords (method standard-method)

Обобщённая функция function-keywords используется для получения спецификаторов именованных параметров для заданного метода.

Аргумент method должен быть объектом метода.

Обобщённая функция возвращает два значение: список явно определённых именованных параметров, и булево значение, которое обозначает содержит ли определение метода &allow-other-keys.


[Макрос] generic-function lambda-list [[option | {method-description}*]]

option ::= (:argument-precedence-order {parameter-name}+ )
    | (declare {declaration}+ )
    | (:documentation string)
    | (:method-combination symbol {arg}* )
    | (:generic-function-class class-name)
    | (:method-class class-name)

method-description ::= (:method {method-qualifier}* specialized-lambda-list {declaration | documentation}* {form}* )

Макрос generic-function создаёт анонимную обобщённую функцию. Обобщённая функция создаётся с набором методов, заданных в части описания методов.

Аргументы option, method-qualifier и specialized-lambda-lisp значат то же, что и в defgeneric.

В качестве результата возвращается объект обобщённой функции.

Если часть описания методов не указана, то обобщённая функция создаётся без методов.

Смотрите defgeneric и defmethod.


[Обобщённая функция] initialize-instance instance &rest initargs
[Главный метод] initialize-instance (instance standard-object) &rest initargs

Обобщённая функция initialize-instance вызывается в функции make-instance для инициализации создаваемого экземпляра объекта. Обобщённая функция initialize-instance применяется к экземпляру класса и инициализационным аргументам.

Системный главный метод initialize-instance инициализирует слоты экземпляра объекта со значениями в соответствие с инициализационными аргументами и формами слотов :initform. Он производит это с помощью вызова обобщённой функции shared-initialize со следующими аргументами: экземпляр класса, t (это указывает на то, что все слоты, для которых не указаны инициализационные аргументы, должны быть инициализированы в соотвествие с их формами :initform и инициализационные аргументы.

Аргумент instance является объектом, который будет инициализирован.

Аргумент initargs состоит из чередующихся имён инициализационных аргументов и их значений.

В качестве результата возвращается модифицированный экземпляр класса.

Программисты могут определять методы initialize-instance для указания дополнительных действий при инициализации экземпляра класса. Если определены только :after методы, они будут вызваны после системного главного метода инициализации и таким образом не будут изменять поведение по-умолчанию initialize-instance.

Смотрите разделы 28.1.9, 28.1.9 и 28.1.9.

Смотрите shared-initialize, make-instance, slot-boundp и slot-makunbound.


[Функция] invalid-method-error method format-string &rest args

Функция invalid-method-error используется для сигнализирования ошибки, в тех случаях, когда у применяемого метода некорректные квалификаторы для типа сочетания методов. Сообщение об ошибке составляется с помощью форматирующей строки и любого количества аргументов к ней. Так как реализации может быть необходимо добавить дополнительную контекстную информацию в сообщение об ошибке, invalid-method-error должна вызываться только внутри динамической продолжительности видимости функции сочетания методов.

Функция invalid-method-error вызывается автоматически, когда метод не удовлетворяет всем шаблонам квалификаторов и предикату в форме define-method-combination. Функция сочетания методов, которая налагает дополнительные ограничения, должна явно вызывать invalid-method-error, если столкнулась с методом, который она не может принять.

Аргумент method является объектом некорректного метода.

Аргумент format-string является форматирующей строкой, которая может передаваться в format, и args является любыми аргументами, необходимыми данной строке.

Возвращает ли invalid-method-error управление или завершается с помощью throw зависит от реализации.

Смотрите define-method-combination .


[Обобщённая функция] make-instance class &rest initargs
[Главный метод] make-instance (class standard-class) &rest initargs
[Главный метод] make-instance (class symbol) &rest initargs

Обобщённая функция make-instance создаёт новый экземпляр заданного класса.

Обобщённая функция make-instance может использоваться, как это описано в разделе 28.1.9.

Аргумент class является объектом класса или символом, который на него указывает. Остальные аргументы формируют список из чередующихся имён инициализационных аргументов и их значений.

Если из вышеуказанного списка выбран второй метод, то он вызывает make-instance с аргументами (find-class class) и initargs.

Инициализационные аргументы проверяются внутри make-instance. Смотрите раздел 28.1.9.

В качестве результата возвращается созданный экземпляр класса.

Для определения новых методов для функции make-instance и соответственно изменения протокола создания объектом может использоваться метаобъектный протокол.

Смотрите раздел 28.1.9.

Смотрите defclass, initialize-instance и class-of.


[Обобщённая функция] make-instances-obsolete class
[Главный метод] make-instances-obsolete (class standard-class)
[Главный метод] make-instances-obsolete (class symbol)

Обобщённая функция make-instance-obsolete вызывается системой автоматически, когда используется defclass для переопределения существующего стандартного класса и изменяется набор или порядок локальных слотов. Также и пользователь может её вызывать явно.

Функция make-instances-obsolete начинает процесс процесс обновления экземпляров класса. В процессе обновления будет вызвана обобщённая функция update-instance-for-redefined-class.

Аргумент class является объектом класса или символом, который на него указывает. Экземпляры данного класса обозначаются как устаревшие.

Если из вышеуказанного списка выбран второй метод, то он вызывает make-instances-obsolete с аргументом (find-class class).

Результатом является модифицированный класс. Результат make-instances-obsolete равен eq аргументу class.

Смотрите раздел ??.

Смотрите update-instance-for-redefined-class.


[Функция] method-combination-error format-string &rest args

Функция method-combination-error используется для сигнализирования ошибки в сочетании методов. Сообщение об ошибке составляется с помощью форматирующей строки и любого количества аргументов к ней. Так как реализации может быть необходимо добавить дополнительную контекстную информацию в сообщение об ошибке, method-error должна вызываться только внутри динамической продолжительности видимости функции сочетания методов.

Аргумент format-string является форматирующей строкой, которая может передаваться в format, и args является любыми аргументами, необходимыми данной строке.

Возвращает ли method-combination-error управление или завершается с помощью throw зависит от реализации.

Смотрите define-method-combination.


[Обобщённая функция] method-qualifiers method
[Главный метод] method-qualifiers (method standard-method)

Обобщённая функция method-qualifiers возвращает список квалификаторов заданного метода.

Аргумент method является объектом метода.

В качестве результата возвращается список квалификаторов метода.

Например:

(setq methods (remove-duplicates methods
                                 :from-end t
                                 :key #’method-qualifiers
                                 :test #’equal))

Смотрите define-method-combination.


[Функция] next-method-p

Локально определённая функция next-method-p может использоваться внутри тела метода для определения того, существует ли следующий метод.

Функция next-method-p не принимает аргументов.

Функция next-method-p возвращает или истину, или ложь.

Также как и call-next-method, функция next-method-p имеет лексическую область видимости (определена только для тела метода) и неограниченную продолжительность видимости.

Смотрите call-next-method.


[Обобщённая функция] no-applicable-method generic-function &rest function-arguments
[Главный метод] no-applicable-method (generic-function t) &rest function-arguments

Обобщённая функция no-applicable-method вызывается, когда обобщённая функция класса standard-generic-function вызывается и не находит не одного применимого метода. По-умолчанию главный метод сигнализирует ошибку.

Обобщённая функция no-applicable-method не предназначена для вызова программистом. Программисты могут писать методы для неё.

Аргумент generic-function является объектом обобщённой функции, в которой не нашлось применимого метода.

Аргумент function-arguments является списком аргументов, которые были переданы в эту функцию.


[Обобщённая функция] no-next-method generic-function method &rest args
[Главный метод] no-next-method (generic-function standard-generic-function) (method standard-method) &rest args

Обобщённая функция no-next-method вызывается функцией call-next-method, когда не было обнаружено последующих методов для вызова. Системный метод для этой функции сигнализирует ошибку.

Обобщённая функция no-next-method не предназначена для вызова программистом. Программисты могут писать методы для неё.

Аргумент generic-function является объектом обобщённой функции, в которой не нашлось последующего метода.

Аргумент method является объектом метода, в котором не нашлось последующего метода.

Аргумент args является списком аргументов, которые были переданы в функцию call-next-method.

Смотрите call-next-method.


[Обобщённая функция] print-object object stream
[Главный метод] print-object (object standard-object) stream

Обобщённая функция print-object выводит в поток текстовое представление объекта. Функция print-object вызывается с помощью системы вывода. Она не должна вызываться пользователем.

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

Первый аргумент — любой Lisp’овый объект. Второй аргумент является потоком, и не может быть t или nil.

Функция print-object в качестве результата возвращает первый аргумент, а, именно, объект.

Методы print-object должны подчиняться специальным переменным управления вывода *print-xxx* для различных xxx. Далее перечислены конкретные детали:

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

В целом, система вывода и методы print-object не должны пересвязывать переменные, управляющие выводом, так как они обрабатываются рекурсивно вместе со структурой объекта, но это зависит от реализации.

В некоторых реализациях аргумент потока, передаваемого в print-object, не является оригинальным потоком, а специальным, который используется системой вывода. Таким образом методы не должны зависеть от сущности этого потока.

Все существующие функции вывода (write, prin1, print, princ, pprint, write-to-string, prin1-to-string, princ-to-string, the ~S and ~A format operations, and the ~B, ~D, ~E, ~F, ~G, ~$, ~O, ~R, and ~X format операции, когда они указаны для нечислового значения) должны использовать обобщённую функцию print-object. Все реализации должны выводить объекты с помощью методов print-object. Так как некоторые классы могут не иметь метода для вывода, реализация может определить один метод по-умолчанию, который унаследуется всеми системными классами.


[Обобщённая функция] reinitialize-instance instance &rest initargs
[Главный метод] reinitialize-instance (instance standard-object) &rest initargs

Обобщённая функция reinitialize-instance может использоваться для изменения значений локальных слотов в соотвествие с инициализационными аргументами. Эта обобщённая функция вызывается метаобъектным протоколом, однако может быть вызвана также и пользователем.

Системный главный метод для reinitialize-instance проверяет корректность инициализационных аргументов и сигнализирует ошибку, в случае некорректности какого-либо из них. Затем метод вызывает обобщённую функцию shared-instance со следующими аргументами: экземпляр, nil (который означает, что ни один слот не должен быть инициализирован в соответствие с формами :initform) и полученные инициализационные аргументы.

Аргумент instance является объектом, который инициализируется.

Аргумент initargs состоит из чередующихся имён инициализационных аргументов и их значений.

В качестве результата возвращается модифицированный экземпляр объекта.

Инициализационные аргументы декларируются в параметре :initarg в defclass или в определении метода для reinitialize-instance или shared-initialize. Имя параметра для каждого спецификаторы именованного параметра в лямбда-списке любого метода для reinitialize-instance или shared-initialize является корректным именем инициализационного аргумента для всех классов, для которых применим данный метод.

Смотрите разделы 28.1.12, 28.1.9, 28.1.9.

Смотрите initialize-instance, slot-boundp, update-instance-for-redefined-class, update-instance-for-different-class, slot-makunbound и shared-initialize.


[Обобщённая функция] remove-method generic-function method
[Главный метод] remove-method (generic-function standard-generic-function) method

Обобщённая функция remove-method удаляет метод из обобщённой функции. Функция деструктивно модифицирует указанную обобщённую функцию и возвращает её в качестве результата.

Аргумент generic-function является объектом обобщённой функции.

Аргумент method является объектом метода. Функция remove-method не сигнализирует ошибку, если метод не принадлежит обобщённой функции.

Результатом является объект обобщённой функции. Результат remove-method равен eq аргументу generic-function.

Смотрите find-method.


[Обобщённая функция] shared-initialize instance slot-names &rest initargs
[Главный метод] shared-initialize (instance standard-object) slot-names &rest initargs

Обобщённая функция shared-initialize используется для заполнения слотов экземпляра класса с помощью инициализационных аргументов и форм :initform. Она вызывается при создании экземпляра, при переинициализации экземпляра, при обновлении экземпляра до переопределённого класса, при обновлении экземпляра до другого класса. Обобщённая функция shared-initialize вызывается системными главными методами initialize-instance, reinitialize-instance, update-instance-for-redefined-class и update-instance-for-different-class.

Обобщённая функция shared-initialize принимает следующие аргументы: экземпляр класса для инициализации, спецификацию множества имён слотов, доступных в данном экземпляре, и любое количество инициализационных аргументов. Аргументы после первых двух, должны формировать список инициализационных аргументов. Системный главный метод shared-initialize инициализирует слоты значениями в соотвествие с инициализационными аргументами и указанными формами :initform. Второй аргумент указывает, какие слоты должны быть инициализированы в соотвествие с их формами :initform, если для них не были инициализационные аргументы.

Системный главный метод ведёт себя так, как сказано ниже вне зависимости от того, является ли слот локальным или разделяемым:

Аргумент instance является объектом, для которого производится инициализация.

Аргумент slot-name указывает слоты, которые будут инициализированы в соотвествие с их формами :initform, если не были указаны инициализационные аргументы. Он может передаваться в трёх разных формах:

Аргумент initargs состоит из чередующихся имён инициализационных аргументов и их значений.

В качестве результата возвращается модифицированный экземпляр объекта.

Инициализационные аргументы корректны, если были указаны в параметре :initarg defclass, или в методе для функции shared-initialize.

Implementations are permitted to optimize :initform forms that neither produce nor depend on side effects by evaluating these forms and storing them into slots before running any initialize-instance methods, rather than by handling them in the primary initialize-instance method. (This optimization might be implemented by having the allocate-instance method copy a prototype instance.)

Implementations are permitted to optimize default initial value forms for initialization arguments associated with slots by not actually creating the complete initialization argument list when the only method that would receive the complete list is the method on standard-object. In this case, default initial value forms can be treated like :initform forms. This optimization has no visible effects other than a performance improvement.

Смотрите разделы 28.1.9, 28.1.9, 28.1.9.

Смотрите initialize-instance, reinitialize-instance, update-instance-for-redefined-class, update-instance-for-different-class, slot-boundp и slot-makunbound.


[Функция] slot-boundp instance slot-name

Функция slot-boundp проверяет связан ли данный слот в экземпляре класса с каким-либо значением.

Аргумент instance является любым объектом. Аргумент slot-name являются символом.

Эта функция позволяет записывать :after методы для initialize-instance для инициализации только тех слотов, которые ещё не были связаны.

Если указанного слота в данном экземпляре не существует, вызывается slot-missing как показано ниже:

(slot-missing (class-of instance)
              instance
              slot-name
              ’slot-makunbound)

Функция slot-boundp реализована с помощью slot-boundp-using-class

Смотрите slot-missing.


[Функция] slot-exists-p object slot-name

Функция slot-exists-p проверяет имеется ли данный слот в экземпляре класса.

Аргумент object является любым объектом. Аргумент slot-name являются символом.

Функция slot-exists-p возвращает истину или ложь.

Функция slot-exists-p реализована с помощью slot-exists-p-using-class


[Функция] slot-makunbound instance slot-name

Функция slot-makunbound устанавливает слот экземпляра instance в несвязанное состояние.

Аргументами slot-makunbound являются: экземпляр и имя слота.

В качестве результата возвращает экземпляр класса.

Если указанного слота в данном экземпляре не существует, вызывается slot-missing как показано ниже:

(slot-missing (class-of instance)
              instance
              slot-name
              ’slot-makunbound)

Функция slot-makunbound реализована с помощью slot-makunbound-using-class. Смотрите slot-missing.


[Обобщённая функция] slot-missing class object slot-name operation &optional new-value
[Главный метод] slot-missing (class t) object slot-name operation &optional new-value

Обобщённая функция slot-missing вызывается, когда происходит попытка доступа к слоту экземпляра класса, метакласс которого равен standard-class, и этот слот отсутствует в данном экземпляре. Метод по-умолчанию сигнализирует ошибку.

Обобщённая функция slot-missing не предназначения для вызова самим программистом. Программист может написать метод для неё.

Аргументами slot-missing являются: класс экземпляра, к слоту которого происходил доступ, сам экземпляр и имя слота и символ, который означает операцию при котором произошёл вызов slot-missing. Необязательный аргумент slot-missing используется тогда, когда операцией являлась попытка установки значения в слот.

Аргументами slot-missing являются: класс экземпляра, к слоту которого происходил доступ, сам экземпляр и имя слота.

Обобщённая функция slot-missing может быть вызвана в процессе вычисления slot-value, (setf slot-value), slot-boundp и slot-makunbound. Для каждой из этих операций для аргумента operation соответствуют символы slot-value, setf, slot-boundp и slot-makunbound.

Набор аргументов (в том числе класса экземпляра) облегчает определении методов метакласса для slot-missing.


[Обобщённая функция] slot-unbound class instance slot-name
[Главный метод] slot-unbound (class t) instance slot-name

Обобщённая функция slot-unbound вызывается, когда происходит попытка чтения несвязанного слота экземпляра класса, метакласс которого равен standard-class. Метод по-умолчанию сигнализирует ошибку.

Обобщённая функция slot-unbound не предназначения для вызова самим программистом. Программист может написать метод для неё. Функция slot-unbound вызывается только функцией slot-value-using-class и соответственно неявно функцией slot-value.

Аргументами slot-unbound являются: класс экземпляра, к слоту которого происходил доступ, сам экземпляр и имя слота.

Если метод, написанный для slot-unbound, возвращает значения, эти значения возвращается в качестве значения вызова оригинальной функции.

Несвязанный слот может получится, если не было формы для параметра слота :initform и этот слот не был позже установлен, или был вызов slot-makunbound с этим слотом.

Смотрите slot-makunbound.


[Функция] slot-value object slot-name

Функция slot-value возвращает значение, содержащееся в слоте с именем slot-name в объекте object. Если слота с данным именем не существует, вызывается slot-missing. Если слот не связан с каким-либо значением, вызывается slot-unbound.

Для изменения значения слота можно использовать setf в связке с slot-value.

При попытке прочтения слота, которого не существует в данном экземпляре класса, вызывается функция slot-missing как показано ниже:

(slot-missing (class-of instance)
              instance
              slot-name
              ’slot-value)

При попытке записи в слот, которого не существует в данном экземпляре класса, вызывается функция slot-missing как показано ниже:

(slot-missing (class-of instance)
              instance
              slot-name
              ’setf
              new-value)

Функция slot-value реализована с использованием slot-value-using-class.

Реализация может оптимизировать slot-value с помощью in-line компиляции.

Смотрите slot-missing и slot-unbound.


[At this point the original CLOS report [57] contained a specification for symbol-macrolet. This specification is omitted here. Instead, a description of symbol-macrolet appears with those of related constructs in chapter 7.—GLS]

[Обобщённая функция] update-instance-for-different-class previous current &rest initargs
[Главный метод] update-instance-for-different-class (previous standard-object) (current standard-object) &rest initargs

Обобщённая функция update-instance-for-different-class не должна вызываться программистом. Однако программисты могут писать для неё методы. Эта функция вызывается только из функции change-class.

Системный главный метод update-instance-for-different-class проверяет корректность инициализационных аргументов и в случае неуспеха сигнализирует ошибку. Затем этот метод инициализирует слоты значениями в соотвествие с инициализационными аргументами, а вновь добавленные слоты значениями в соотвествие с их формами :initform. Это происходит в вызове функции shared-initialize со следующими аргументами: экземпляр, список имён вновь добавленных слотов, и полученные инициализационные аргументы. Вновь добавленные слоты — это локальные слоты, для которых не существовали одноимённые слоты в старом предыдущем классе.

Для указания дополнительных действии при обновлении экземпляра класса можно создать методы для update-instance-for-different-class. Если определены только :after методы, то они запускаются после системного главного метода и таким образом не воздействует на поведение системы по-умолчанию.

Аргументы для update-instance-for-different-class вычисляются в change-class. Когда для экземпляра вызывается change-class, создаётся копия этого экземпляра. Она временно содержит старые значения слотов. Эти аргументы имеют динамическую продолжительность видимости внутри change-class. Если после выхода из update-instance-for-different-class на них остались ссылки, результат непредсказуем. Второй аргумент для update-instance-for-different-class, current, является модифицируемым оригинальным экземпляром.

Использование previous заключается в получении старых значений слотов с помощью slot-value, или with-slots или обобщённой функции чтения, или для запуска других методов которые применимы для экземпляров оригинального класса.

Аргумент initargs состоит из чередующихся имён инициализационных аргументов и их значений.

Значение update-instance-for-different-class в change-class игнорируется.

Смотрите пример для функции change-class.

Инициализационные аргументы корректны, если были указаны в параметре :initarg или в методах для update-instance-for-different-class или shared-initialize.

Для инициализации слота по-особому могут быть определены методы для update-instance-for-different-class. Поведение функции change-class по-умолчанию описано в разделе 28.1.11.

Смотрите разделы 28.1.11, 28.1.9 и 28.1.9.

Смотрите change-class и shared-initialize.


[Обобщённая функция] update-instance-for-redefined-class instance added-slots discarded-slots property-list &rest initargs
[Главный метод] update-instance-for-redefined-class (instance standard-object) added-slots discarded-slots property-list &rest initargs

Обобщённая функция update-instance-for-redefined-class не должна вызываться программистом. Однако программисты могут писать для неё методы. Эта функция вызывается системой при использовании make-instance-obsolete.

Системный главный метод update-instance-for-redefined-class проверяет корректность инициализационных аргументов и в случае неуспеха сигнализирует ошибку. Затем этот метод инициализирует слоты значениями в соотвествие с инициализационными аргументами, а вновь добавленные слоты значениями в соотвествие с их формами :initform. Это происходит в вызове функции shared-initialize со следующими аргументами: экземпляр, список имён вновь добавленных слотов, и полученные инициализационные аргументы. Вновь добавленные слоты — это локальные слоты, для которых не существовали одноимённые слоты в старом предыдущем классе.

При вызове make-instance-obsolete или при переопределении класса и обновлении экземпляра, создаётся список свойств, который содержит имена слотов и значения всех удалённых слотов из оригинального экземпляра. Структура экземпляра трансформируется то, чтобы удовлетворять новому определению класса. Аргумент update-instance-for-redefined-class: трансформированный экземпляр, список имён новых слотов, список имён старых слотов, список свойств, содержащий имена и значения старых слотов. В данных список также включается слоты, которые были локальными и стали разделяемыми.

Аргумент initargs состоит из чередующихся имён инициализационных аргументов и их значений.

Значение, возвращаемое из update-instance-for-redefined-class игнорируется.

Инициализационные аргументы корректны, если были указаны в параметре :initarg или в методах для update-instance-for-redefined-class или shared-initialize.

Смотрите разделы 28.1.10, 28.1.9 и 28.1.9.

Смотрите shared-initialize и make-instance-obsolete.

(defclass position () ())

(defclass x-y-position (position)
  ((x :initform 0 :accessor position-x)
   (y :initform 0 :accessor position-y)))

;;; Оказывается полярные координаты используются чаще декартовых
;;; координат, так что представление меняется, и добавляются
;;; некоторые новые методы доступа.

(defmethod update-instance-for-redefined-class :before
           ((pos x-y-position) added deleted plist &key)
  ;; Transform the x-y coordinates to polar coordinates
  ;; and store into the new slots.
  (let ((x (getf plist ’x))
        (y (getf plist ’y)))
    (setf (position-rho pos) (sqrt (+ (* x x) (* y y)))
          (position-theta pos) (atan y x))))

(defclass x-y-position (position)
    ((rho :initform 0 :accessor position-rho)
     (theta :initform 0 :accessor position-theta)))

;;; Все экземпляры старого класса x-y-position будут обновлены
;;; автоматически.

;;; Новое представление имеет вид и вкус старого.

(defmethod position-x ((pos x-y-position))
   (with-slots (rho theta) pos (* rho (cos theta))))

(defmethod (setf position-x) (new-x (pos x-y-position))
   (with-slots (rho theta) pos
     (let ((y (position-y pos)))
       (setq rho (sqrt (+ (* new-x new-x) (* y y)))
             theta (atan y new-x))
       new-x)))

(defmethod position-y ((pos x-y-position))
   (with-slots (rho theta) pos (* rho (sin theta))))

(defmethod (setf position-y) (new-y (pos x-y-position))
   (with-slots (rho theta) pos
     (let ((x (position-x pos)))
       (setq rho (sqrt (+ (* x x) (* new-y new-y)))
             theta (atan new-y x))
       new-y)))


[Макрос] with-accessors ({slot-entry}*) instance-form{declaration}* {form}*

Макрос with-accessors создаёт лексическое окружение, в котором указанные слоты доступны с помощью аксессоров, как если бы они были переменными. Для доступа к указанному слоту макрос with-accessors вызывает соответствующий аксессор. Для установки значения в слот может использоваться и setf, и setq.

Результатом является то, что вернут формы указанные в аргументе form.

Например:

(with-accessors ((x position-x) (y position-y)) p1
  (setq x y))

Выражение with-accessors в форме:

(with-accessors (slot1 ... slotn) instance
  declaration1 ... declarationm)
  form1 ... formk)

раскрывается в эквивалентное выражение

(let ((in instance))
  (symbol-macrolet ((variable1 (accessor1 in))
                    ...
                    (variablen (accessorn in)))
    declaration1 ... declarationm)
    form1 ... formk)

[X3J13 voted in March 1989 to modify the definition of symbol-macrolet substantially and also voted to allow declarations before the body of symbol-macrolet but with peculiar treatment of special and type declarations. The syntactic changes are reflected in this definition of with-accessors.—GLS]

Смотрите with-slots и symbol-macrolet.


[Макрос] with-slots ({slot-entry}*) instance-form {declaration}* {form}*

slot-entry ::= slot-name | (variable-name slot-name)

Макрос with-slots создаёт лексический контекст для ссылки на заданные слоты как если бы они были переменными. Внутри такого контекста значение слота может быть получено просто указанием имени слота, как если бы это была лексически связанная переменная. Для установки значения в слот может использоваться и setf, и setq.

Макрос with-slots транслирует вхождения имени слота как переменной в вызов slot-value.

Результатом является то, что вернут формы указанные в аргументе form.

Например:

(with-slots (x y) position-1
  (sqrt (+ (* x x) (* y y))))

(with-slots ((x1 x) (y1 y)) position-1
  (with-slots ((x2 x) (y2 y)) position-2
    (psetf x1 x2
           y1 y2))))

(with-slots (x y) position
  (setq x (1+ x)
        y (1+ y)))

Выражение with-slots в форме:

(with-slots (slot-entry1 ... slot-entryn) instance
  declaration1 ... declarationm)
  form1 ... formk)

раскрывается в эквивалентный код

(let ((in instance))
  (symbol-macrolet (Q1 ... Qn)
    declaration1 ... declarationm)
    form1 ... formk)

где Qj является

(slot-entryj (slot-value inslot-entryj))

если slot-entryj является символом, или Qj является

(variable-namej (slot-value inslot-namej))

если slot-entryj принадлежит форме (variable-namej slot-namej).

[X3J13 voted in March 1989 to modify the definition of symbol-macrolet substantially and also voted to allow declarations before the body of symbol-macrolet but with peculiar treatment of special and type declarations. The syntactic changes are reflected in this definition of with-slots.—GLS]

Смотрите with-accessors и symbol-macrolet.