summaryrefslogtreecommitdiff
path: root/doc/lispref/functions.texi
diff options
context:
space:
mode:
authorStefan Monnier <monnier@iro.umontreal.ca>2022-03-28 10:53:14 -0400
committerStefan Monnier <monnier@iro.umontreal.ca>2022-03-28 10:53:14 -0400
commit52d5771e0a803f57b8cdd7675bf15f2f9b946039 (patch)
tree26ff950a2c8527da7a2438d7ed1825555dbf2e40 /doc/lispref/functions.texi
parent3f19a23c1f60757c54a0ec7d84c625d83766ee08 (diff)
downloademacs-52d5771e0a803f57b8cdd7675bf15f2f9b946039.tar.gz
Add OClosures, a cross between functions and structs
We here just add the new type. It is not fully self-contained. It requires cooperation from `cconv.el` on the one hand, and it hijacks the docstring info to hold the type of OClosure objects. This does imply that OClosures can't have docstrings, tho this limitation will be lifted in subsequent patches. * lisp/emacs-lisp/oclosure.el: New file. * test/lisp/emacs-lisp/oclosure-tests.el: New file. * doc/lispref/functions.texi (OClosures): New section. * src/eval.c (Ffunction): Accept symbols instead of strings for docstrings. * src/doc.c (store_function_docstring): Avoid overwriting an OClosure type. * lisp/emacs-lisp/cconv.el (cconv--convert-function): Tweak ordering of captured variables. (cconv-convert): Add case for `oclosure--fix-type`.
Diffstat (limited to 'doc/lispref/functions.texi')
-rw-r--r--doc/lispref/functions.texi111
1 files changed, 111 insertions, 0 deletions
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 207919ea645..70337d4c4a8 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -22,6 +22,7 @@ define them.
* Function Cells:: Accessing or setting the function definition
of a symbol.
* Closures:: Functions that enclose a lexical environment.
+* OClosures:: Function objects
* Advising Functions:: Adding to the definition of a function.
* Obsolete Functions:: Declaring functions obsolete.
* Inline Functions:: Functions that the compiler will expand inline.
@@ -1509,6 +1510,116 @@ exposed to the rest of the Lisp world is considered an internal
implementation detail. For this reason, we recommend against directly
examining or altering the structure of closure objects.
+@node OClosures
+@section Open Closures
+
+Traditionally, functions are opaque objects which offer no other
+functionality but to call them. Emacs Lisp functions aren't fully
+opaque since you can extract some info out of them such as their
+docstring, their arglist, or their interactive spec, but they are
+mostly opaque. This is usually what we want, but occasionally we need
+functions to expose a bit more information about themselves.
+
+OClosures are functions which carry additional type information,
+and expose some information in the form of slots which you can access
+via accessor functions.
+
+They are defined in two steps: first @code{oclosure-define} is used to
+define new OClosure types by specifying the slots carried by those
+OClosures, and then @code{oclosure-lambda} is used to create an
+OClosure object of a given type.
+
+Say we want to define keyboard macros, i.e. interactive functions
+which re-execute a sequence of key events. You could do it with
+a plain function as follows:
+@example
+(defun kbd-macro (key-sequence)
+ (lambda (&optional arg)
+ (interactive "P")
+ (execute-kbd-macro key-sequence arg)))
+@end example
+But with such a definition there is no easy way to extract the
+@var{key-sequence} from that function, for example to print it.
+
+We can solve this problem using OClosures as follows. First we define
+the type of our keyboard macros (to which we decided to add
+a @code{counter} slot while at it):
+@example
+(oclosure-define kbd-macro
+ "Keyboard macro."
+ keys (counter :mutable t))
+@end example
+After which we can rewrite our @code{kbd-macro} function:
+@example
+(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))))
+@end example
+As you can see, the @code{keys} and @code{counter} slots of the
+OClosure can be accessed as local variables from within the body
+of the OClosure. But we can now also access them from outside of the
+body of the OClosure, for example to describe a keyboard macro:
+@example
+(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))))
+@end example
+Where @code{kbd-macro--keys} and @code{kbd-macro--counter} are
+accessor functions generated by the @code{oclosure-define} macro.
+
+@defmac oclosure-define name &optional docstring &rest slots
+This macro defines a new OClosure type along with accessor functions
+for its slots. @var{name} can be a symbol (the name of
+the new type), or a list of the form @code{(@var{name} . @var{type-props})} in
+which case @var{type-props} is a list of additional properties.
+@var{slots} is a list of slot descriptions where each slot can be
+either a symbol (the name of the slot) or it can be of the form
+@code{(@var{slot-name} . @var{slot-props})} where @var{slot-props} is
+a property list.
+
+For each slot, the macro creates an accessor function named
+@code{@var{name}--@var{slot-name}}. By default slots are immutable.
+If you need a slot to be mutable, you need to specify it with the
+@code{:mutable} slot property, after which it can be mutated for
+example with @code{setf}.
+
+Beside slot accessors, the macro can create a predicate and
+functional update functions according to @var{type-props}:
+a @code{(:predicate @var{pred-name})} in the @var{type-props} causes
+the definition of a predicate function under the name @var{pred-name},
+and @code{(:copier @var{copier-name} @var{copier-arglist})} causes the
+definition of a functional update function which takes an OClosure of
+type @var{name} as first argument and returns a copy of it with the
+slots named in @var{copier-arglist} modified to the value passed in the
+corresponding argument.
+@end defmac
+
+@defmac oclosure-lambda (type . slots) arglist &rest body
+This macro creates an anonymous OClosure of type @var{type}.
+@var{slots} should be a list of elements of the form @code{(@var{slot-name}
+@var{exp})}.
+At run time, each @var{exp} is evaluated, in order, after which
+the OClosure is created with its slots initialized with the
+resulting values.
+
+When called as a function, the OClosure will accept arguments
+according to @var{arglist} and will execute the code in @var{body}.
+@var{body} can refer to the value of any of its slot directly as if it
+were a local variable that had been captured by static scoping.
+@end defmac
+
+@defun oclosure-type object
+This function returns the OClosure type (a symbol) of @var{object} if it is an
+OClosure, and nil otherwise.
+@end defun
+
+
@node Advising Functions
@section Advising Emacs Lisp Functions
@cindex advising functions