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 * Function Cells:: Accessing or setting the function definition
of a symbol. of a symbol.
* Closures:: Functions that enclose a lexical environment. * Closures:: Functions that enclose a lexical environment.
* OClosures:: Function objects with meta-data.
* Advising Functions:: Adding to the definition of a function. * Advising Functions:: Adding to the definition of a function.
* Obsolete Functions:: Declaring functions obsolete. * Obsolete Functions:: Declaring functions obsolete.
* Inline Functions:: Defining functions that the compiler * Inline Functions:: Defining functions that the compiler

View file

@ -22,7 +22,7 @@ define them.
* Function Cells:: Accessing or setting the function definition * Function Cells:: Accessing or setting the function definition
of a symbol. of a symbol.
* Closures:: Functions that enclose a lexical environment. * 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. * Advising Functions:: Adding to the definition of a function.
* Obsolete Functions:: Declaring functions obsolete. * Obsolete Functions:: Declaring functions obsolete.
* Inline Functions:: Functions that the compiler will expand inline. * Inline Functions:: Functions that the compiler will expand inline.
@ -1581,56 +1581,69 @@ examining or altering the structure of closure objects.
@node OClosures @node OClosures
@section Open Closures @section Open Closures
@cindex oclosures
@cindex open closures
Traditionally, functions are opaque objects which offer no other Traditionally, functions are opaque objects which offer no other
functionality but to call them. Emacs Lisp functions aren't fully functionality but to call them. (Emacs Lisp functions aren't fully
opaque since you can extract some info out of them such as their opaque since you can extract some info out of them such as their
docstring, their arglist, or their interactive spec, but they are docstring, their arglist, or their interactive spec, but they are
mostly opaque. This is usually what we want, but occasionally we need still mostly opaque.) This is usually what we want, but occasionally
functions to expose a bit more information about themselves. we need functions to expose a bit more information about themselves.
OClosures are functions which carry additional type information, @dfn{Open closures}, or @dfn{OClosures} for short, are function
and expose some information in the form of slots which you can access objects which carry additional type information and expose some
information about themselves in the form of slots which you can access
via accessor functions. via accessor functions.
They are defined in two steps: first @code{oclosure-define} is used to OClosures are defined in two steps: first you use
define new OClosure types by specifying the slots carried by those @code{oclosure-define} to define a new OClosure type by specifying the
OClosures, and then @code{oclosure-lambda} is used to create an slots carried by the OClosures of this type, and then you use
OClosure object of a given type. @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 @example
(defun kbd-macro (key-sequence) (defun kbd-macro (key-sequence)
(lambda (&optional arg) (lambda (&optional arg)
(interactive "P") (interactive "P")
(execute-kbd-macro key-sequence arg))) (execute-kbd-macro key-sequence arg)))
@end example @end example
@noindent
But with such a definition there is no easy way to extract the But with such a definition there is no easy way to extract the
@var{key-sequence} from that function, for example to print it. @var{key-sequence} from that function, for example to print it.
We can solve this problem using OClosures as follows. First we define We can solve this problem using OClosures as follows. First we define
the type of our keyboard macros (to which we decided to add the type of our keyboard macros (to which we decided to add
a @code{counter} slot while at it): a @code{counter} slot while at it):
@example @example
(oclosure-define kbd-macro (oclosure-define kbd-macro
"Keyboard macro." "Keyboard macro."
keys (counter :mutable t)) keys (counter :mutable t))
@end example @end example
@noindent
After which we can rewrite our @code{kbd-macro} function: After which we can rewrite our @code{kbd-macro} function:
@example @example
(defun kbd-macro (key-sequence) (defun kbd-macro (key-sequence)
(oclosure-lambda (kbd-macro (keys key-sequence) (counter 0)) (oclosure-lambda (kbd-macro (keys key-sequence) (counter 0))
(&optional arg) (&optional arg)
(interactive "p") (interactive "P")
(execute-kbd-macro keys arg) (execute-kbd-macro keys arg)
(setq counter (1+ counter)))) (setq counter (1+ counter))))
@end example @end example
@noindent
As you can see, the @code{keys} and @code{counter} slots of the As you can see, the @code{keys} and @code{counter} slots of the
OClosure can be accessed as local variables from within the body 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 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: body of the OClosure, for example to describe a keyboard macro:
@example @example
(defun describe-kbd-macro (km) (defun describe-kbd-macro (km)
(if (not (eq 'kbd-macro (oclosure-type 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))) (counter (kbd-macro--counter km)))
(message "Keys=%S, called %d times" keys counter)))) (message "Keys=%S, called %d times" keys counter))))
@end example @end example
@noindent
Where @code{kbd-macro--keys} and @code{kbd-macro--counter} are 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 This macro defines a new OClosure type along with accessor functions
for its slots. @var{name} can be a symbol (the name of for its @var{slots}. @var{oname} can be a symbol (the name of the new
the new type), or a list of the form @code{(@var{name} . @var{type-props})} in type), or a list of the form
which case @var{type-props} is a list of additional properties. @w{@code{(@var{oname} . @var{type-props})}}, in which case
@var{slots} is a list of slot descriptions where each slot can be @var{type-props} is a list of additional properties of this oclosure
either a symbol (the name of the slot) or it can be of the form type. @var{slots} is a list of slot descriptions where each slot can
@code{(@var{slot-name} . @var{slot-props})} where @var{slot-props} is be either a symbol (the name of the slot) or it can be of the form
a property list. @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 @table @code
@code{@var{name}--@var{slot-name}}. By default slots are immutable. @item (:predicate @var{pred-name})
If you need a slot to be mutable, you need to specify it with the This requests creation of a predicate function named @var{pred-name}.
@code{:mutable} slot property, after which it can be mutated for This function will be used to recognize OClosures of the type
example with @code{setf}. @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 For each slot in @var{slots}, the @code{oclosure-define} macro creates
functional update functions according to @var{type-props}: an accessor function named @code{@var{oname}--@var{slot-name}}; these
a @code{(:predicate @var{pred-name})} in the @var{type-props} causes can be used to access the values of the slots. The slot definitions
the definition of a predicate function under the name @var{pred-name}, in @var{slots} can specify the following properties of the slots:
and @code{(:copier @var{copier-name} @var{copier-arglist})} causes the
definition of a functional update function which takes an OClosure of @table @code
type @var{name} as first argument and returns a copy of it with the @item :mutable @var{val}
slots named in @var{copier-arglist} modified to the value passed in the By default, slots are immutable, but if you specify the
corresponding argument. @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 @end defmac
@defmac oclosure-lambda (type . slots) arglist &rest body @defmac oclosure-lambda (type . slots) arglist &rest body
This macro creates an anonymous OClosure of type @var{type}. This macro creates an anonymous OClosure of type @var{type}, which
@var{slots} should be a list of elements of the form @code{(@var{slot-name} should have been defined with @code{oclosure-define}. @var{slots}
@var{exp})}. should be a list of elements of the form
At run time, each @var{exp} is evaluated, in order, after which @w{@code{(@var{slot-name} @var{expr})}}. At run time, each @var{expr}
the OClosure is created with its slots initialized with the is evaluated, in order, after which the OClosure is created with its
resulting values. slots initialized with the resulting values.
When called as a function, the OClosure will accept arguments When called as a function (@pxref{Calling Functions}), the OClosure
according to @var{arglist} and will execute the code in @var{body}. created by this macro will accept arguments according to @var{arglist}
@var{body} can refer to the value of any of its slot directly as if it and will execute the code in @var{body}. @var{body} can refer to the
were a local variable that had been captured by static scoping. value of any of its slot directly as if it were a local variable that
had been captured by static scoping.
@end defmac @end defmac
@defun oclosure-type object @defun oclosure-type object
This function returns the OClosure type (a symbol) of @var{object} if it is an This function returns the OClosure type (a symbol) of @var{object} if
OClosure, and @code{nil} otherwise. it is an OClosure, and @code{nil} otherwise.
@end defun @end defun