12.17.2 Определение новых форм setf

В этом разделе описывается, как определить новые формы, над которыми setf может работать.

Макрос: gv-define-simple-setter name setter &optional fix-return

Этот макрос позволяет легко определять методы setf для простых случаев. name — это имя функции, макроса или специальной формы. Вы можете использовать этот макрос всякий раз, когда name имеет соответствующую функцию setter, которая обновляет его, например, (gv-define-simple-setter car setcar).

Этот макрос преобразует вызов вида

(setf (name args…) value)

в

(setter args… value)

Такой вызов setf документирован как возвращающий value. Это не проблема, например, с car и setcar, потому что setcar возвращает установленное значение. Если ваша функция setter не возвращает value, используйте не-nil значение для аргумента fix-return в gv-define-simple-setter. Это расширяется до чего-то эквивалентного

(let ((temp value))
  (setter args… temp)
  temp)

тем самым гарантируя, что он возвращает правильный результат.

Макрос: gv-define-setter name arglist &rest body

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

Этот макрос расширяет форму (setf (name args…) value) путем сначала привязки форм аргументов setf (value args…) в соответствии с arglist, а затем выполнения body. body должен вернуть форму Lisp, которая выполняет присваивание, и, наконец, возвращает установленное значение. Пример использования этого макроса:

(gv-define-setter caar (val x) `(setcar (car ,x) ,val))
Макрос: gv-define-expander name handler

Для большего контроля над расширением можно использовать макрос gv-define-expander. Например, таким образом можно реализовать изменяемую substring:

(gv-define-expander substring
  (lambda (do place from &optional to)
    (gv-letplace (getter setter) place
      (macroexp-let2* (from to)
        (funcall do `(substring ,getter ,from ,to)
                 (lambda (v)
                   (macroexp-let2* (v)
                     `(progn
                        ,(funcall setter `(cl--set-substring
                                           ,getter ,from ,to ,v))
                        ,v))))))))
Макрос: gv-letplace (getter setter) place &rest body

Макрос gv-letplace может быть полезен при определении макросов, которые выполняют действия, аналогичные setf; например, макрос incf из Common Lisp можно реализовать таким образом:

(defmacro incf (place &optional n)
  (gv-letplace (getter setter) place
    (macroexp-let2* ((v (or n 1)))
      (funcall setter `(+ ,v ,getter)))))

getter будет привязан к копируемому выражению, которое возвращает значение place. setter будет привязан к функции, которая принимает выражение v и возвращает новое выражение, устанавливающее place в v. body должен возвращать выражение Emacs Lisp, манипулирующее place через getter и setter.

Подробности см. в исходном файле gv.el.

Функция: make-obsolete-generalized-variable obsolete-name current-name when

Эта функция заставляет компилятор байт предупредить о том, что обобщенная переменная obsolete-name устарела. Если current-name — это символ, то сообщение предупреждения гласит использовать current-name вместо obsolete-name. Если current-name — это строка, то это сообщение. when должен быть строкой, указывающей, когда переменная была впервые объявлена устаревшей (обычно строка с номером версии).

Примечание Common Lisp: Common Lisp определяет другой способ указания поведения setf функции, а именно функции setf, имена которых являются списками (setf name) вместо символов. Например, (defun (setf foo) …) определяет функцию, которая используется, когда setf применяется к foo. Emacs не поддерживает это. Использование setf для формы, для которой не было определено соответствующее расширение, является ошибкой во время компиляции. В Common Lisp это не ошибка, поскольку функция (setf func) может быть определена позже.