Minor copyedits of documentation of OClosures

* doc/lispref/functions.texi (OClosures): Improve wording,
indexing, and markup; add details.
This commit is contained in:
Eli Zaretskii 2023-03-03 15:23:22 +02:00
parent 3a651773d2
commit 119b3a4dba
2 changed files with 94 additions and 50 deletions

View file

@ -569,6 +569,7 @@ Functions
* Function Cells:: Accessing or setting the function definition
of a symbol.
* Closures:: Functions that enclose a lexical environment.
* OClosures:: Function objects with meta-data.
* Advising Functions:: Adding to the definition of a function.
* Obsolete Functions:: Declaring functions obsolete.
* Inline Functions:: Defining functions that the compiler

View file

@ -22,7 +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
* OClosures:: Function objects with meta-data.
* Advising Functions:: Adding to the definition of a function.
* Obsolete Functions:: Declaring functions obsolete.
* Inline Functions:: Functions that the compiler will expand inline.
@ -1581,56 +1581,69 @@ examining or altering the structure of closure objects.
@node OClosures
@section Open Closures
@cindex oclosures
@cindex open closures
Traditionally, functions are opaque objects which offer no other
functionality but to call them. Emacs Lisp functions aren't fully
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.
still 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
@dfn{Open closures}, or @dfn{OClosures} for short, are function
objects which carry additional type information and expose some
information about themselves 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.
OClosures are defined in two steps: first you use
@code{oclosure-define} to define a new OClosure type by specifying the
slots carried by the OClosures of this type, and then you use
@code{oclosure-lambda} to create an OClosure object of a given type.
Let's say we want to define keyboard macros, i.e.@: interactive
functions which re-execute a sequence of key events (@pxref{Keyboard
Macros}). You could do it with a plain function as follows:
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
@noindent
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
@noindent
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")
(interactive "P")
(execute-kbd-macro keys arg)
(setq counter (1+ counter))))
@end example
@noindent
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)))
@ -1639,53 +1652,83 @@ body of the OClosure, for example to describe a keyboard macro:
(counter (kbd-macro--counter km)))
(message "Keys=%S, called %d times" keys counter))))
@end example
@noindent
Where @code{kbd-macro--keys} and @code{kbd-macro--counter} are
accessor functions generated by the @code{oclosure-define} macro.
accessor functions generated by the @code{oclosure-define} macro for
oclosures whose type is @code{kbd-macro}.
@defmac oclosure-define name &optional docstring &rest slots
@defmac oclosure-define oname &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 its @var{slots}. @var{oname} can be a symbol (the name of the new
type), or a list of the form
@w{@code{(@var{oname} . @var{type-props})}}, in which case
@var{type-props} is a list of additional properties of this oclosure
type. @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
@w{@code{(@var{slot-name} . @var{slot-props})}}, where
@var{slot-props} is a property list of the corresponding slot
@var{slot-name}.
The OClosure type's properties specified by @var{type-props} can
include the following:
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}.
@table @code
@item (:predicate @var{pred-name})
This requests creation of a predicate function named @var{pred-name}.
This function will be used to recognize OClosures of the type
@var{oname}. If this type property is not specified,
@code{oclosure-define} will generate a default name for the
predicate.
@item (:parent @var{otype})
This makes type @var{otype} of OClosures be the parent of the type
@var{oname}. The OClosures of type @var{oname} inherit the
@var{slots} defined by their parent type.
@c FIXME: Is the above description of :parent correct?
@item (:copier @var{copier-name} @var{copier-args})
This causes the definition of a functional update function, knows as
the @dfn{copier}, which takes an OClosure of type @var{oname} as its
first argument and returns a copy of it with the slots named in
@var{copier-args} modified to contain the value passed in the
corresponding argument in the actual call to @var{copier-name}.
@end table
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.
For each slot in @var{slots}, the @code{oclosure-define} macro creates
an accessor function named @code{@var{oname}--@var{slot-name}}; these
can be used to access the values of the slots. The slot definitions
in @var{slots} can specify the following properties of the slots:
@table @code
@item :mutable @var{val}
By default, slots are immutable, but if you specify the
@code{:mutable} property with a non-@code{nil} value, the slot can be
mutated, for example with @code{setf} (@pxref{Setting Generalized
Variables}).
@c FIXME: Some rationale and meaning of immutable slot is probably in
@c order here.
@item :type @var{val-type}
This specifies the type of the values expected to appear in the slot.
@c FIXME: What will happen if the value is of a different type? error?
@end table
@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.
This macro creates an anonymous OClosure of type @var{type}, which
should have been defined with @code{oclosure-define}. @var{slots}
should be a list of elements of the form
@w{@code{(@var{slot-name} @var{expr})}}. At run time, each @var{expr}
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.
When called as a function (@pxref{Calling Functions}), the OClosure
created by this macro 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 @code{nil} otherwise.
This function returns the OClosure type (a symbol) of @var{object} if
it is an OClosure, and @code{nil} otherwise.
@end defun