Открытые замыкания
Традиционно функции — это непрозрачные объекты, которые не предоставляют никаких возможностей, кроме вызова. (Функции Emacs Lisp не полностью непрозрачны, поскольку из них можно извлечь некоторую информацию, например строку документации, список аргументов или описание интерактивного вызова, но в основном они всё же непрозрачны.) Обычно именно этого мы и хотим, но иногда нам нужно, чтобы функции предоставляли немного больше информации о себе.
Открытые замыкания, или кратко OClosure, — это функциональные объекты, которые содержат дополнительные сведения о типе и предоставляют некоторую информацию о себе в виде слотов, к которым можно обращаться с помощью функций доступа.
OClosure определяются в два этапа: сначала используется oclosure-define для определения нового типа OClosure с указанием слотов, содержащихся в OClosure этого типа, а затем используется oclosure-lambda для создания объекта OClosure заданного типа.
Предположим, мы хотим определить клавиатурные макросы, то есть интерактивные функции, которые повторно выполняют последовательность событий клавиатуры (см. Клавиатурные макросы). Это можно сделать с помощью обычной функции следующим образом:
(defun kbd-macro (key-sequence)
(lambda (&optional arg)
(interactive "P")
(execute-kbd-macro key-sequence arg)))
Однако при таком определении нет простого способа извлечь из этой функции key-sequence, например, чтобы вывести его на печать.
Эту проблему можно решить с помощью OClosure следующим образом. Сначала мы определяем тип наших клавиатурных макросов (заодно решив добавить слот counter):
(oclosure-define kbd-macro "Keyboard macro." keys (counter :mutable t))
После этого мы можем переписать нашу функцию kbd-macro:
(defun kbd-macro (key-sequence)
(oclosure-lambda (kbd-macro (keys key-sequence) (counter 0))
(&optional arg)
(interactive "P")
(execute-kbd-macro keys arg)
(setq counter (1+ counter))))
Как видите, к слотам keys и counter объекта OClosure можно обращаться как к локальным переменным из тела OClosure. Но теперь к ним можно обращаться и вне тела OClosure, например, чтобы описать клавиатурный макрос:
(defun describe-kbd-macro (km)
(if (not (eq 'kbd-macro (oclosure-type km)))
(message "Not a keyboard macro")
(let ((keys (kbd-macro--keys km))
(counter (kbd-macro--counter km)))
(message "Keys=%S, called %d times" keys counter))))
Здесь kbd-macro--keys и kbd-macro--counter — функции доступа, создаваемые макросом oclosure-define для OClosure типа kbd-macro.
- Макрос: oclosure-define oname &optional docstring &rest slots
-
Этот макрос определяет новый тип OClosure вместе с функциями доступа к его слотам slots. oname может быть символом (именем нового типа) или списком вида
(oname . type-props); в последнем случае type-props — это список дополнительных свойств данного типа OClosure. slots — это список описаний слотов, где каждый слот может быть символом (именем слота) или иметь вид(slot-name . slot-props), причём slot-props — это список свойств соответствующего слота slot-name. Свойства типа OClosure, заданные с помощью type-props, могут включать следующие:(:predicate pred-name)Этот параметр указывает создать функцию-предикат с именем pred-name. Эта функция будет использоваться для распознавания OClosure типа oname. Если это свойство типа не указано,
oclosure-defineсгенерирует имя предиката по умолчанию.(:parent otype)Это свойство задаёт тип OClosure otype в качестве родительского для типа oname. OClosure типа oname наследуют слоты slots, определённые родительским типом.
(:copier copier-name copier-args)Это свойство приводит к определению функции функционального обновления, называемой копировщиком. Она принимает OClosure типа oname в качестве первого аргумента и возвращает его копию, в которой слоты, указанные в copier-args, изменены и содержат значения, переданные соответствующими аргументами при вызове copier-name.
Для каждого слота в slots макрос
oclosure-defineсоздаёт функцию доступа с именемoname--slot-name; с её помощью можно получать значения слотов. В описаниях слотов в slots можно указывать следующие свойства::mutable valПо умолчанию слоты неизменяемы, но если указать свойство
:mutableсо значением, отличным отnil, слот можно изменять, например с помощьюsetf(см. Макросsetf).:type val-typeЭто свойство задаёт тип значений, которые ожидаются в слоте.
- Макрос: oclosure-lambda (type . slots) arglist &rest body
-
Этот макрос создаёт анонимный OClosure типа type, который должен быть определён с помощью
oclosure-define. slots должен быть списком элементов вида(slot-name expr). Во время выполнения каждое expr вычисляется по порядку, после чего создаётся OClosure со слотами, инициализированными полученными значениями.При вызове в качестве функции (см. Вызов функций) OClosure, созданный этим макросом, принимает аргументы в соответствии с arglist и выполняет код из body. В body можно напрямую обращаться к значениям любых слотов, как если бы это были локальные переменные, захваченные статической областью видимости.
- Функция: oclosure-type object
Если object является OClosure, эта функция возвращает его тип (символ), а в противном случае —
nil.
С OClosure связана ещё одна функция — oclosure-interactive-form, которая позволяет некоторым типам OClosure динамически вычислять интерактивные формы. См. oclosure-interactive-form.
Copyright © 1990-1996, 1998-2026 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/OClosures.html