diff options
| author | Earl Hyatt | 2025-03-12 23:01:49 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2025-03-31 14:29:48 -0400 |
| commit | e04d1dafc700813c835ae4e45af4e104c49e8875 (patch) | |
| tree | 405296248ee9da13e065213d8f88cfe317a7bc81 | |
| parent | a97a61b630624f5a6ec917db92e2985c56b20aa0 (diff) | |
| download | emacs-e04d1dafc700813c835ae4e45af4e104c49e8875.tar.gz emacs-e04d1dafc700813c835ae4e45af4e104c49e8875.zip | |
Add cl-with-accessors
* lisp/emacs-lisp/cl-macs.el (cl-with-accessors): New macro.
* doc/misc/cl.texi (Structures): Mention the new macro.
* test/lisp/emacs-lisp/cl-macs-tests.el (cl-lib-struct-with-accessors):
New Test.
* etc/NEWS (New macro 'cl-with-accessors'.): Mention the macro.
This macro is useful when making repeated use of a structures accessor
functions, such as reading from a slot and then writing to a slot. It
is similar to 'with-slots' from EIEIO, but uses accessor functions
instead of slot names.
| -rw-r--r-- | doc/misc/cl.texi | 49 | ||||
| -rw-r--r-- | etc/NEWS | 10 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 44 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/cl-macs-tests.el | 15 |
4 files changed, 118 insertions, 0 deletions
diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi index e51e245c736..7219494391b 100644 --- a/doc/misc/cl.texi +++ b/doc/misc/cl.texi | |||
| @@ -4066,6 +4066,55 @@ A documentation string describing the slot. | |||
| 4066 | 4066 | ||
| 4067 | Other slot options are currently ignored. | 4067 | Other slot options are currently ignored. |
| 4068 | 4068 | ||
| 4069 | @defmac cl-with-accessors name bindings body@dot{} | ||
| 4070 | You can use @code{cl-with-accessors} to lexically define symbols as | ||
| 4071 | expressions calling the given accessor functions on a single instance of | ||
| 4072 | a structure or class defined by @code{cl-defstruct} or @code{defclass} | ||
| 4073 | (@pxref{eieio}). This can simplify code that repeatedly accesses slots. | ||
| 4074 | With it, you can use @code{setf} and @code{setq} on the symbols like | ||
| 4075 | normal variables, modifying the values in the structure. Unlike the | ||
| 4076 | macro @code{with-slots} (@pxref{Accessing Slots,,,eieio,EIEIO}), because | ||
| 4077 | the symbol expands to a function call, @code{cl-with-accessors} can be | ||
| 4078 | used with any generalized variable that can take a single argument, such | ||
| 4079 | as @code{cl-first} and @code{cl-rest}. | ||
| 4080 | @end defmac | ||
| 4081 | |||
| 4082 | @example | ||
| 4083 | ;; Using accessors with long, clear names without the macro: | ||
| 4084 | (defun internal-normalization (person) | ||
| 4085 | "Correct the values of the slots in PERSON to be as expected." | ||
| 4086 | ;; Check the values of the structure: | ||
| 4087 | (when (equal (person-optional-secondary-data person) "") | ||
| 4088 | (setf (person-optional-secondary-data person) nil)) | ||
| 4089 | (when (null (person-access-settings person)) | ||
| 4090 | (setf (person-access-settings person) 'default)) | ||
| 4091 | (when (< (long-accessor-name-that-can-become-unreadable-when-repeated | ||
| 4092 | person) | ||
| 4093 | 9) | ||
| 4094 | (cl-incf (long-accessor-name-that-can-become-unreadable-when-repeated | ||
| 4095 | person) | ||
| 4096 | 100)) | ||
| 4097 | ;; And so on before returning the structure: | ||
| 4098 | person) | ||
| 4099 | |||
| 4100 | ;; Using accessors with long, clear names with the macro: | ||
| 4101 | (defun internal-normalization (person) | ||
| 4102 | "Correct the values of the slots in PERSON to be as expected." | ||
| 4103 | (cl-with-accessors ((secondary-data person-optional-secondary-data) | ||
| 4104 | (access-settings person-access-settings) | ||
| 4105 | (short-name person-much-longer-accessor-name)) | ||
| 4106 | person | ||
| 4107 | ;; Check the values of the structure: | ||
| 4108 | (when (equal secondary-data "") | ||
| 4109 | (setf secondary-data nil)) | ||
| 4110 | (when (null access-settings) | ||
| 4111 | (setf access-settings 'default)) | ||
| 4112 | (when (< short-name 9) | ||
| 4113 | (cl-incf short-name 100)) | ||
| 4114 | ;; And so on before returning the structure: | ||
| 4115 | person)) | ||
| 4116 | @end example | ||
| 4117 | |||
| 4069 | For obscure historical reasons, structure options take a different | 4118 | For obscure historical reasons, structure options take a different |
| 4070 | form than slot options. A structure option is either a keyword | 4119 | form than slot options. A structure option is either a keyword |
| 4071 | symbol, or a list beginning with a keyword symbol possibly followed | 4120 | symbol, or a list beginning with a keyword symbol possibly followed |
| @@ -1611,6 +1611,16 @@ New faces have been added to 'icomplete-vertical-mode': | |||
| 1611 | - 'icomplete-vertical-unselected-prefix-indicator-face' controls the | 1611 | - 'icomplete-vertical-unselected-prefix-indicator-face' controls the |
| 1612 | appearance of unselected candidate prefixes. | 1612 | appearance of unselected candidate prefixes. |
| 1613 | 1613 | ||
| 1614 | ** CL-Lib | ||
| 1615 | |||
| 1616 | +++ | ||
| 1617 | *** New macro 'cl-with-accessors'. | ||
| 1618 | This macro is similar to 'with-slots', but uses accessor functions | ||
| 1619 | instead of slot names. It is useful when slots' accessor functions are | ||
| 1620 | used repeatedly, such as reading from a slot and then writing to that | ||
| 1621 | slot. Symbol macros are created for the accessor functions using | ||
| 1622 | 'cl-symbol-macrolet', so that they can be used with 'setq' and 'setf'. | ||
| 1623 | |||
| 1614 | ** Miscellaneous | 1624 | ** Miscellaneous |
| 1615 | 1625 | ||
| 1616 | --- | 1626 | --- |
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index 960f2e6742b..cc1c6a6a5ad 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el | |||
| @@ -2576,6 +2576,50 @@ See also `macroexp-let2'." | |||
| 2576 | collect `(,(car name) ,gensym)) | 2576 | collect `(,(car name) ,gensym)) |
| 2577 | ,@body))))) | 2577 | ,@body))))) |
| 2578 | 2578 | ||
| 2579 | ;;;###autoload | ||
| 2580 | (defmacro cl-with-accessors (bindings instance &rest body) | ||
| 2581 | "Use BINDINGS as function calls on INSTANCE inside BODY. | ||
| 2582 | |||
| 2583 | This macro helps when writing code that makes repeated use of the | ||
| 2584 | accessor functions of a structure or object instance, such as those | ||
| 2585 | created by `cl-defstruct' and `defclass'. | ||
| 2586 | |||
| 2587 | BINDINGS is a list of (NAME ACCESSOR) pairs. Inside BODY, NAME is | ||
| 2588 | treated as the function call (ACCESSOR INSTANCE) using | ||
| 2589 | `cl-symbol-macrolet'. NAME can be used with `setf' and `setq' as a | ||
| 2590 | generalized variable. Because of how the accessor is used, | ||
| 2591 | `cl-with-accessors' can be used with any generalized variable that can | ||
| 2592 | take a single argument, such as `car' and `cdr'. | ||
| 2593 | |||
| 2594 | See also the macro `with-slots' described in the Info | ||
| 2595 | node `(eieio)Accessing Slots', which is similar, but uses slot names | ||
| 2596 | instead of accessor functions. | ||
| 2597 | |||
| 2598 | \(fn ((NAME ACCESSOR) ...) INSTANCE &rest BODY)" | ||
| 2599 | (declare (debug [(&rest (symbolp symbolp)) form body]) | ||
| 2600 | (indent 2)) | ||
| 2601 | (cond ((null body) | ||
| 2602 | (macroexp-warn-and-return "`cl-with-accessors' used with empty body" | ||
| 2603 | nil 'empty-body)) | ||
| 2604 | ((null bindings) | ||
| 2605 | (macroexp-warn-and-return "`cl-with-accessors' used without accessors" | ||
| 2606 | (macroexp-progn body) | ||
| 2607 | 'suspicious)) | ||
| 2608 | (t | ||
| 2609 | (cl-once-only (instance) | ||
| 2610 | (let ((symbol-macros)) | ||
| 2611 | (dolist (b bindings) | ||
| 2612 | (pcase b | ||
| 2613 | (`(,(and (pred symbolp) var) | ||
| 2614 | ,(and (pred symbolp) accessor)) | ||
| 2615 | (push `(,var (,accessor ,instance)) | ||
| 2616 | symbol-macros)) | ||
| 2617 | (_ | ||
| 2618 | (error "Malformed `cl-with-accessors' binding: %S" b)))) | ||
| 2619 | `(cl-symbol-macrolet | ||
| 2620 | ,symbol-macros | ||
| 2621 | ,@body)))))) | ||
| 2622 | |||
| 2579 | ;;; Multiple values. | 2623 | ;;; Multiple values. |
| 2580 | 2624 | ||
| 2581 | ;;;###autoload | 2625 | ;;;###autoload |
diff --git a/test/lisp/emacs-lisp/cl-macs-tests.el b/test/lisp/emacs-lisp/cl-macs-tests.el index b4b939d3d31..ed6b1c2e4d4 100644 --- a/test/lisp/emacs-lisp/cl-macs-tests.el +++ b/test/lisp/emacs-lisp/cl-macs-tests.el | |||
| @@ -541,6 +541,21 @@ collection clause." | |||
| 541 | (should (mystruct-p (cl-lib--con-1))) | 541 | (should (mystruct-p (cl-lib--con-1))) |
| 542 | (should (mystruct-p (cl-lib--con-2)))) | 542 | (should (mystruct-p (cl-lib--con-2)))) |
| 543 | 543 | ||
| 544 | (ert-deftest cl-lib-struct-with-accessors () | ||
| 545 | (let ((x (make-mystruct :abc 1 :def 2))) | ||
| 546 | (cl-with-accessors ((abc mystruct-abc) | ||
| 547 | (def mystruct-def)) | ||
| 548 | x | ||
| 549 | (should (= abc 1)) | ||
| 550 | (should-error (setf abc 99)) | ||
| 551 | (should (= def 2)) | ||
| 552 | (setf def 3) | ||
| 553 | (should (= def 3)) | ||
| 554 | (setq def 4) | ||
| 555 | (should (= def 4))) | ||
| 556 | (should (= 4 (mystruct-def x))) | ||
| 557 | (should (= 1 (mystruct-abc x))))) | ||
| 558 | |||
| 544 | (ert-deftest cl-lib-arglist-performance () | 559 | (ert-deftest cl-lib-arglist-performance () |
| 545 | ;; An `&aux' should not cause lambda's arglist to be turned into an &rest | 560 | ;; An `&aux' should not cause lambda's arglist to be turned into an &rest |
| 546 | ;; that's parsed by hand. | 561 | ;; that's parsed by hand. |