4.7 Определение новых спецификаторов

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

[Макрос] deftype name lambda-list [[{declaration}* | doc-string]] {form}*

Данный макрос весьма схож с формой defmacro: name является символом, который будет определять имя будущего спецификатора типа, lambda-list является лямбда списком (и может содержать маркеры &optional и &rest) и forms составляют тело функции. Если мы рассмотрим спецификатор типа, как список, содержащий имя и несколько форм аргументов, то формы аргументов (невычисленные) будут связаны с параметрами из лямбда списка lambda-list. Затем будут выполнены формы тела, как неявный progn, и значение последней формы будет интерпретировано, как новый спецификатор типа, для которого исходный спецификатор является аббревиатурой. name возвращается, как значение формы deftype.

deftype отличается от defmacro в том, что если для необязательного &optional параметра значение по умолчанию initform не задано, то используется *, а не nil.

Если указана необязательная строка документации doc-string, тогда она присоединяется к имени name, как строка документации типа type. Смотрите documentation.

Вот несколько примеров использования deftype:

(deftype mod (n) ‘(integer 0 (,n)))

(deftype list () ’(or null cons))

(deftype square-matrix (&optional type size)
  "SQUARE-MATRIX includes all square two-dimensional arrays."
  ‘(array ,type (,size ,size)))

(square-matrix short-float 7) означает (array short-float (7 7))

(square-matrix bit) означает (array bit (* *))

Если имя типа заданного с помощью deftype используется просто как символ спецификатора типа, тогда это имя интерпретируется, как список, задающий тип с аргументами по-умолчанию *. Например, используя код выше, square-matrix будет означать (array * (* *)), то есть множество двумерных

В таком случае к несчастью нарушается правило о том, что количество строк в матрице должно совпадать с количеством столбцов. (square-matrix bit) имеет такую же проблему. Лучшим решением будет:

(defun equidimensional (a)
  (or (< (array-rank a) 2)
      (apply #’= (array-dimensions a))))

(deftype square-matrix (&optional type size)
  ‘(and (array ,type (,size ,size))
        (satisfies equidimensional)))

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

Тогда как обычно эта форма используется на верхнем уровне, ее можно использовать и внутри других форм. Например, deftype может определять функцию типа внутри лексического, а не глобального окружения.