9.1 Синтаксис декларации

Конструкция declare используется для встраивания деклараций внутрь выполняемого кода. Глобальные декларации и декларации, вычисленные программой, устанавливаются конструкцией proclaim.

Для установки глобальных деклараций часто бывает удобен макрос declaim, он в отличии от proclaim не вычисляет свои аргументы.

[Специальный оператор] declare {decl-spec}*

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

Вычисление декларации является ошибкой. Операторы, которые позволяют использовать декларации, явно проверяют их наличие.

Макровызовы могут раскрываться в декларации, при условии, что макровызов указан в том месте, где могут быть указаны декларации. (Однако, макровызов не может использоваться в форме declare на месте decl-spec.)

Декларация может использовать только явно в теле соответсвтующего специального оператора в виде списка, car которого равен символу declare.

Каждая форма decl-spec является списком, у которого car элемент это символ, указывающий на тип декларации. Декларации могут быть разделены на два класса: одни относятся к связыванию переменных, другие нет. (Декларация special является исключением, она попадает в оба класса, это будет описано ниже.) Те, которые касаются связываний переменных, применяются только к связываниям, созданным в форме, в которой они используются. Например, в

(defun foo (x)
  (declare (type float x)) ...
  (let ((x ’a)) ...)
  ...)

декларация type применяется только для внешнего связывания x, а не для связывания, созданного в let.

Декларации, которые не воздействуют на связывания переменных, воздействуют на весь код тела в оператора. Например,

(defun foo (x y) (declare (notinline floor)) ...)

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

Некоторые операторы содержат части кода, которые, правильнее говоря, не являются телом этого оператора. Это например код инициализации переменных и форма результата для циклов. Во всех случаях, такой дополнительный код оказывается под воздействием всеобъемлемых деклараций, которые указаны перед телом оператора. Невсеобъемлемые декларации не оказывают воздействия на этот код, за исключением (конечно) ситуаций, когда код находится в области действия переменной, для которой использовалась декларация. Например:

(defun few (x &optional (y *print-circle*))
  (declare (special *print-circle*))
  ...)

Ссылка на *print-circle* в первой строке примера является специальной, так как указана соответствующая декларация.

(defun nonsense (k x z)
  (foo z x)               ;Первый вызов foo
  (let ((j (foo k x))     ;Второй вызов foo
        (x (* k k)))
    (declare (inline foo) (special x z))
    (foo x j z)))         ;Третий вызов foo

В этом примере, декларация inline применяется только ко второму и третьему вызову foo. Декларация special для переменной x указывает, что форма let создаст специальное связывание для x и тем самым ссылки в теле формы также будут специальными. Ссылка на x во втором вызове foo является специальной. Ссылка на x в первом вызове foo является локальной, а не специальной. Декларация special для z указывает на то, что ссылка в вызове foo будет специальной. Это значит ссылка не будет указывать на параметр функции nonsense, так как для параметра декларации special не указано. (Декларация special переменной z указывается не в теле defun, а в теле внутренней конструкции let. Таким образом она не воздействует на связывание параметра функции.)

X3J13 voted in January 1989 to replace the rules concerning the scope of declarations occurring at the head of a special operator or lambda-expression:

Note that the distinction between pervasive and non-pervasive declarations is eliminated. An important change from the first edition is that “initialization” forms are specifically not included as part of the body under the first rule; on the other hand, in many cases initialization forms may fall within the scope of certain declarations under the second rule.

X3J13 also voted in January 1989 to change the interpretation of type declarations (see section 9.2).

These changes affect the interpretation of some of the examples from the first edition.

(defun foo (x)
  (declare (type float x)) ...
  (let ((x ’a)) ...)
  ...)

Under the interpretation approved by X3J13, the type declaration applies to both bindings of x. More accurately, the type declaration is considered to apply to variable references rather than bindings, and the type declaration refers to every reference in the body of foo to a variable named x, no matter to what binding it may refer.

(defun foo (x y) (declare (notinline floor)) ...)

This example of the use of notinline stands unchanged, but the following slight extension of it would change:

(defun foo (x &optional (y (floor x)))
  (declare (notinline floor)) ...)

Under first edition rules, the notinline declaration would be considered to apply to the call to floor in the initialization form for y. Under the interpretation approved by X3J13, the notinline would not apply to that particular call to floor. Instead the user must write something like

(defun foo (x &optional (y (locally (declare (notinline floor))
                                    (floor x))))
  (declare (notinline floor)) ...)

or perhaps

(locally (declare (notinline floor))
  (defun foo (x &optional (y (floor x))) ...))

Similarly, the special declaration in

(defun few (x &optional (y *print-circle*))
  (declare (special *print-circle*))
  ...)

is not considered to apply to the reference in the initialization form for y in few. As for the nonsense example,

(defun nonsense (k x z)
  (foo z x)               ;First call to foo
  (let ((j (foo k x))     ;Second call to foo
        (x (* k k)))
    (declare (inline foo) (special x z))
    (foo x j z)))         ;Third call to foo

under the interpretation approved by X3J13, the inline declaration is no longer considered to apply to the second call to foo, because it is in an initialization form, which is no longer considered in the scope of the declaration. Similarly, the reference to x in that second call to foo is no longer taken to be a special reference, but a local reference to the second parameter of nonsense.


locally выполняет формы form как неявный progn и возвращает одно или несколько значений последней формы.

[Специальный оператор] locally {declaration}* {form}*

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

(locally
  (declare (optimize (safety 3) (space 3) (debug 3) (speed 1)))
  (defun foo (x &optional (y (abs x)) (z (sqrt y)))
    (bar x y z)))

Без уверенности, что это работает, можно записать что-то вроде этого:


(defun foo (x &optional (y (locally
                              (declare (optimize (safety 3)
                                                 (space 3)
                                                 (debug 3)
                                                 (speed 1)))
                              (abs x)))
                         (z (locally
                              (declare (optimize (safety 3)
                                                 (space 3)
                                                 (debug 3)
                                                 (speed 1)))
                              (sqrt y))))
  (locally
    (declare (optimize (safety 3) (space 3) (debug 3) (speed 1)))
    (bar x y z)))


[Функция] proclaim decl-spec

Функция proclaim в качестве аргумента принимает decl-spec, и применяет указания в глобальном пространстве. (Такие глобальные декларации называются прокламации.) Так как proclaim является функцией, то её аргумент вычисляется всегда. Это позволяет программам вычислять декларацию и затем для применения помещать её в вызов proclaim.

Любые упоминаемые имена переменных указывают на значения динамических переменных. Например, выполненная прокламация

(proclaim ’(type float tolerance))

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

Прокламации содержат универсальные декларации, которые всегда действуют, кроме случаев сокрытия их локальными декларациями. Например,

(proclaim ’(inline floor))

рекомендует то, что floor должна быть встроена в места её вызова. Но в ситуации

(defun foo (x y) (declare (notinline floor)) ...)

floor будет вызываться как отдельная функция, так как локальная декларация скрыла прокламацию.

X3J13 voted in January 1989 to clarify that such shadowing does not occur in the case of type declarations. If there is a local type declaration for a special variable and there is also a global proclamation for that same variable, then the value of the variable within the scope of the local declaration must be a member of the intersection of the two declared types. This is consistent with the treatment of nested local type declarations on which X3J13 also voted in January 1989 .

Специальный случай, когда proclaim обрабатывает special, то decl-spec применяется ко всем связываниям и ссылкам на упомянутую переменную. Например, после

(proclaim ’(special x))

в определении функции

(defun example (x) ...)

параметр x будет связан, как специальная (динамическая) переменная, а не лексическая (статическая). Такой приём должен использоваться аккуратно. Обычный способ определить глобальную специальную переменную это использовать defvar или defparameter.


[Макрос] declaim {decl-spec}*

Этот макрос синтаксически похож на declare и семантически на proclaim. Это выполняемая форма и она может использоваться везде, где может proclaim. Однако, формы decl-spec не вычисляются.

Если вызов этого макроса произошёл на верхнем уровне в файле, обрабатываемом компилятором, то прокламации также будут выполнены во время компиляции. As with other defining macros, it is unspecified whether or not the compile-time side effects of a declaim persist after the file has been compiled (see section 24.1). FIXME