]> git.eshelyaron.com Git - emacs.git/commitdiff
New macros incf and decf
authorStefan Kangas <stefankangas@gmail.com>
Sat, 22 Feb 2025 16:32:31 +0000 (17:32 +0100)
committerEshel Yaron <me@eshelyaron.com>
Sun, 23 Feb 2025 08:18:58 +0000 (09:18 +0100)
* lisp/emacs-lisp/cl-lib.el (cl-incf, cl-decf): Move macros from here...
* lisp/emacs-lisp/gv.el (incf, decf): ...to here.  Make old names into
aliases, documented as deprecated.
* lisp/obsolete/cl.el: Don't alias incf and decf.

* test/lisp/emacs-lisp/cl-lib-tests.el (cl-lib-test-incf)
(cl-lib-test-decf): Move tests from here...
* test/lisp/emacs-lisp/gv-tests.el (gv-incf, gv-decf): ...to here.

* doc/lispref/numbers.texi (Arithmetic Operations):
* lisp/emacs-lisp/shortdoc.el (number): Document incf and decf.

* doc/lispref/variables.texi (Multisession Variables):
* doc/misc/cl.texi (Organization, Modify Macros, Modify Macros)
(Modify Macros, Macro Bindings, For Clauses, Property Lists)
(Structures, Efficiency Concerns, Obsolete Setf Customization): Delete
cl-incf and cl-decf documentation, moving any relevant parts to lispref.
Delete some parts that seem to primarily regard implementation details
that do not warrant inclusion in lispref.  Update all examples to use
incf/decf.

(cherry picked from commit 95fee880e45184f4820e9704b75887ef2d91bd01)

doc/lispref/numbers.texi
doc/lispref/variables.texi
doc/misc/cl.texi
lisp/emacs-lisp/cl-lib.el
lisp/emacs-lisp/gv.el
lisp/emacs-lisp/shortdoc.el
lisp/obsolete/cl.el
test/lisp/emacs-lisp/cl-lib-tests.el
test/lisp/emacs-lisp/gv-tests.el

index 17fa1e05feef836d25d76f2290203c580062ca7c..29105959ecda1c5c3b08aa1972fa7254b3f12246 100644 (file)
@@ -668,8 +668,8 @@ foo
      @result{} 4
 @end example
 
-If you want to increment the variable, you must use @code{setq},
-like this:
+If you want to increment the variable, you must use @code{setq} (or
+@code{incf}), like this:
 
 @example
 (setq foo (1+ foo))
@@ -681,6 +681,21 @@ like this:
 This function returns @var{number-or-marker} minus 1.
 @end defun
 
+@defmac incf place &optional delta
+This macro increments the number stored in @var{place} by one, or
+by @var{delta} if specified.  The incremented value is returned.
+
+@var{place} can be a symbol or a generalized variable, @xref{Generalized
+Variables}.  For example, @code{(incf i)} is equivalent to
+@code{(setq i (1+ i))}, and @code{(incf (car x) 2)} is equivalent to
+@code{(setcar x (+ (car x) 2))}.
+@end defmac
+
+@defmac decf place &optional delta
+This macro decrements the number stored in @var{place} by one, or
+by @var{delta} if specified.  The decremented value is returned.
+@end defmac
+
 @defun + &rest numbers-or-markers
 This function adds its arguments together.  When given no arguments,
 @code{+} returns 0.
index ce7bd27eef6acc5fa864dfd5aa803515b9f947c3..6f2dbd6b3ec1d06d0aa8bf2c84b7834e499ddda0 100644 (file)
@@ -3130,7 +3130,7 @@ If the multisession variable is synchronized, setting it may update
 the value first.  For instance:
 
 @lisp
-(cl-incf (multisession-value foo-bar))
+(incf (multisession-value foo-bar))
 @end lisp
 
 This first checks whether the value has changed in a different
index f95900a085e6bcc11259e12a30d934fe811d659c..8fb308e64a50e47987b8fe17db2996d1c310aecc 100644 (file)
@@ -168,9 +168,6 @@ and information about the package.  This file is relatively compact.
 
 @item cl-extra.el
 This file contains the larger, more complex or unusual functions.
-It is kept separate so that packages which only want to use Common
-Lisp fundamentals like the @code{cl-incf} function won't need to pay
-the overhead of loading the more advanced functions.
 
 @item cl-seq.el
 This file contains most of the advanced functions for operating
@@ -197,8 +194,8 @@ this package prior to Emacs 24.3.  Nowadays, it is replaced by
 but use different function names (in fact, @file{cl.el} mainly just
 defines aliases to the @file{cl-lib.el} definitions).  Where
 @file{cl-lib.el} defines a function called, for example,
-@code{cl-incf}, @file{cl.el} uses the same name but without the
-@samp{cl-} prefix, e.g., @code{incf} in this example.  There are a few
+@code{cl-first}, @file{cl.el} uses the same name but without the
+@samp{cl-} prefix, e.g., @code{first} in this example.  There are a few
 exceptions to this.  First, functions such as @code{cl-defun} where
 the unprefixed version was already used for a standard Emacs Lisp
 function.  In such cases, the @file{cl.el} version adds a @samp{*}
@@ -1028,7 +1025,7 @@ generalized variables.
 
 @menu
 * Setf Extensions::    Additional @code{setf} places.
-* Modify Macros::      @code{cl-incf}, @code{cl-rotatef}, @code{cl-letf}, @code{cl-callf}, etc.
+* Modify Macros::      @code{cl-rotatef}, @code{cl-letf}, @code{cl-callf}, etc.
 @end menu
 
 @node Setf Extensions
@@ -1085,52 +1082,6 @@ Specifically, all subforms are evaluated from left to right, then
 all the assignments are done (in an undefined order).
 @end defmac
 
-@defmac cl-incf place &optional x
-This macro increments the number stored in @var{place} by one, or
-by @var{x} if specified.  The incremented value is returned.  For
-example, @code{(cl-incf i)} is equivalent to @code{(setq i (1+ i))}, and
-@code{(cl-incf (car x) 2)} is equivalent to @code{(setcar x (+ (car x) 2))}.
-
-As with @code{setf}, care is taken to preserve the ``apparent'' order
-of evaluation.  For example,
-
-@example
-(cl-incf (aref vec (cl-incf i)))
-@end example
-
-@noindent
-appears to increment @code{i} once, then increment the element of
-@code{vec} addressed by @code{i}; this is indeed exactly what it
-does, which means the above form is @emph{not} equivalent to the
-``obvious'' expansion,
-
-@example
-(setf (aref vec (cl-incf i))
-      (1+ (aref vec (cl-incf i))))   ; wrong!
-@end example
-
-@noindent
-but rather to something more like
-
-@example
-(let ((temp (cl-incf i)))
-  (setf (aref vec temp) (1+ (aref vec temp))))
-@end example
-
-@noindent
-Again, all of this is taken care of automatically by @code{cl-incf} and
-the other generalized-variable macros.
-
-As a more Emacs-specific example of @code{cl-incf}, the expression
-@code{(cl-incf (point) @var{n})} is essentially equivalent to
-@code{(forward-char @var{n})}.
-@end defmac
-
-@defmac cl-decf place &optional x
-This macro decrements the number stored in @var{place} by one, or
-by @var{x} if specified.
-@end defmac
-
 @defmac cl-pushnew x place @t{&key :test :test-not :key}
 This macro inserts @var{x} at the front of the list stored in
 @var{place}, but only if @var{x} isn't present in the list already.
@@ -1243,8 +1194,8 @@ It does the bindings in sequential rather than parallel order.
 This is the ``generic'' modify macro.  It calls @var{function},
 which should be an unquoted function name, macro name, or lambda.
 It passes @var{place} and @var{args} as arguments, and assigns the
-result back to @var{place}.  For example, @code{(cl-incf @var{place}
-@var{n})} is the same as @code{(cl-callf + @var{place} @var{n})}.
+result back to @var{place}.  For example, @code{(incf @var{place}
+@var{n})} could be implemented as @code{(cl-callf + @var{place} @var{n})}.
 Some more examples:
 
 @example
@@ -1264,7 +1215,7 @@ equivalent to @code{(cl-callf2 cons @var{x} @var{place})}.
 @end defmac
 
 The @code{cl-callf} and @code{cl-callf2} macros serve as building
-blocks for other macros like @code{cl-incf}, and @code{cl-pushnew}.
+blocks for other macros like @code{cl-pushnew}.
 The @code{cl-letf} and @code{cl-letf*} macros are used in the processing
 of symbol macros; @pxref{Macro Bindings}.
 
@@ -1401,7 +1352,7 @@ replaced by @var{expansion}.
 @example
 (setq bar '(5 . 9))
 (cl-symbol-macrolet ((foo (car bar)))
-  (cl-incf foo))
+  (incf foo))
 bar
      @result{} (6 . 9)
 @end example
@@ -1426,7 +1377,7 @@ expansion of another macro:
                     body))))
 
 (setq mylist '(1 2 3 4))
-(my-dolist (x mylist) (cl-incf x))
+(my-dolist (x mylist) (incf x))
 mylist
      @result{} (2 3 4 5)
 @end example
@@ -1440,14 +1391,14 @@ shown here expands to
 @example
 (cl-loop for G1234 on mylist do
       (cl-symbol-macrolet ((x (car G1234)))
-        (cl-incf x)))
+        (incf x)))
 @end example
 
 @noindent
 which in turn expands to
 
 @example
-(cl-loop for G1234 on mylist do (cl-incf (car G1234)))
+(cl-loop for G1234 on mylist do (incf (car G1234)))
 @end example
 
 @xref{Loop Facility}, for a description of the @code{cl-loop} macro.
@@ -1999,7 +1950,7 @@ a @code{setf}-able ``reference'' onto the elements of the list
 rather than just a temporary variable.  For example,
 
 @example
-(cl-loop for x in-ref my-list do (cl-incf x))
+(cl-loop for x in-ref my-list do (incf x))
 @end example
 
 @noindent
@@ -2940,7 +2891,7 @@ The @code{get} and @code{cl-get} functions are also @code{setf}-able.
 The fact that @code{default} is ignored can sometimes be useful:
 
 @example
-(cl-incf (cl-get 'foo 'usage-count 0))
+(incf (cl-get 'foo 'usage-count 0))
 @end example
 
 Here, symbol @code{foo}'s @code{usage-count} property is incremented
@@ -4051,7 +4002,7 @@ calling @code{(person-first-name @var{p})}, @code{(person-age
 slots by using @code{setf} on any of these place forms, for example:
 
 @example
-(cl-incf (person-age birthday-boy))
+(incf (person-age birthday-boy))
 @end example
 
 You can create a new @code{person} by calling @code{make-person},
@@ -4459,29 +4410,14 @@ user to modify @var{place}.
 Many of the advanced features of this package, such as @code{cl-defun},
 @code{cl-loop}, etc., are implemented as Lisp macros.  In
 byte-compiled code, these complex notations will be expanded into
-equivalent Lisp code which is simple and efficient.  For example,
-the form
-
-@example
-(cl-incf i n)
-@end example
-
-@noindent
-is expanded at compile-time to the Lisp form
-
-@example
-(setq i (+ i n))
-@end example
-
-@noindent
-which is the most efficient way of doing this operation
-in Lisp.  Thus, there is no performance penalty for using the more
-readable @code{cl-incf} form in your compiled code.
+equivalent Lisp code which is simple and efficient.  Thus, there is no
+performance penalty for using the more readable form in your compiled
+code.
 
 @emph{Interpreted} code, on the other hand, must expand these macros
 every time they are executed.  For this reason it is strongly
 recommended that code making heavy use of macros be compiled.
-A loop using @code{cl-incf} a hundred times will execute considerably
+A loop using @code{cl-first} a hundred times will execute considerably
 faster if compiled, and will also garbage-collect less because the
 macro expansion will not have to be generated, used, and thrown away a
 hundred times.
@@ -4907,7 +4843,7 @@ call to @code{make-adder} itself.
 @example
 (defun make-counter ()
   (lexical-let ((n 0))
-    (cl-function (lambda (&optional (m 1)) (cl-incf n m)))))
+    (cl-function (lambda (&optional (m 1)) (incf n m)))))
 (setq count-1 (make-counter))
 (funcall count-1 3)
      @result{} 3
@@ -5052,7 +4988,7 @@ In Emacs, these are obsolete, replaced by various features of
 
 @defmac define-modify-macro name arglist function [doc-string]
 This macro defines a ``read-modify-write'' macro similar to
-@code{cl-incf} and @code{cl-decf}.  You can replace this macro
+@code{incf} and @code{decf}.  You can replace this macro
 with @code{gv-letplace}.
 
 The macro @var{name} is defined to take a @var{place} argument
@@ -5180,8 +5116,8 @@ For example, the simple form of @code{defsetf} is shorthand for
 
 The Lisp form that is returned can access the arguments from
 @var{arglist} and @var{store-var} in an unrestricted fashion;
-macros like @code{cl-incf} that invoke this
-setf-method will insert temporary variables as needed to make
+macros that invoke this
+setf-method should insert temporary variables as needed to make
 sure the apparent order of evaluation is preserved.
 
 Another standard example:
@@ -5245,11 +5181,7 @@ temporary variables.  In the setf-methods generated by
 @code{defsetf}, the second return value is simply the list of
 arguments in the place form, and the first return value is a
 list of a corresponding number of temporary variables generated
-@c FIXME I don't think this is true anymore.
-by @code{cl-gensym}.  Macros like @code{cl-incf} that
-use this setf-method will optimize away most temporaries that
-turn out to be unnecessary, so there is little reason for the
-setf-method itself to optimize.
+by @code{cl-gensym}.
 @end defmac
 
 @node GNU Free Documentation License
index e9f1c02e4abc9406dfae144a727a44b0fca2561b..2ab2f9f9022e769a2f61ea7bd2182c2bfd377f47 100644 (file)
@@ -105,29 +105,27 @@ a future Emacs interpreter will be able to use it.")
 ;; can safely be used in init files.
 
 ;;;###autoload
-(defmacro cl-incf (place &optional x)
+(defalias 'cl-incf #'incf
   "Increment PLACE by X (1 by default).
 PLACE may be a symbol, or any generalized variable allowed by `setf'.
 The return value is the incremented value of PLACE.
 
 If X is specified, it should be an expression that should
-evaluate to a number."
-  (declare (debug (place &optional form)))
-  (if (symbolp place)
-      (list 'setq place (if x (list '+ place x) (list '1+ place)))
-    (list 'cl-callf '+ place (or x 1))))
+evaluate to a number.
+
+This macro is considered deprecated in favor of the built-in macro
+`incf' that was added in Emacs 31.1.")
 
-(defmacro cl-decf (place &optional x)
+(defalias 'cl-decf #'decf
   "Decrement PLACE by X (1 by default).
 PLACE may be a symbol, or any generalized variable allowed by `setf'.
 The return value is the decremented value of PLACE.
 
 If X is specified, it should be an expression that should
-evaluate to a number."
-  (declare (debug cl-incf))
-  (if (symbolp place)
-      (list 'setq place (if x (list '- place x) (list '1- place)))
-    (list 'cl-callf '- place (or x 1))))
+evaluate to a number.
+
+This macro is considered deprecated in favor of the built-in macro
+`decf' that was added in Emacs 31.1.")
 
 (defmacro cl-pushnew (x place &rest keys)
   "Add X to the list stored in PLACE unless X is already in the list.
index 5ec21195ab77550494baeb9f53a0ca6fb69caab5..84b301d2b0aec1d5d0f75bb812b9e67c66bca389 100644 (file)
@@ -315,17 +315,29 @@ The return value is the last VAL in the list.
 ;;       `(if (member ,v ,getter) nil
 ;;          ,(funcall setter `(cons ,v ,getter))))))
 
-;; (defmacro gv-inc! (place &optional val)
-;;   "Increment PLACE by VAL (default to 1)."
-;;   (declare (debug (gv-place &optional form)))
-;;   (gv-letplace (getter setter) place
-;;     (funcall setter `(+ ,getter ,(or val 1)))))
+;;;###autoload
+(defmacro incf (place &optional delta)
+  "Increment PLACE by DELTA (default to 1).
 
-;; (defmacro gv-dec! (place &optional val)
-;;   "Decrement PLACE by VAL (default to 1)."
-;;   (declare (debug (gv-place &optional form)))
-;;   (gv-letplace (getter setter) place
-;;     (funcall setter `(- ,getter ,(or val 1)))))
+The DELTA is first added to PLACE, and then stored in PLACE.
+Return the incremented value of PLACE.
+
+See also `decf'."
+  (declare (debug (gv-place &optional form)))
+  (gv-letplace (getter setter) place
+    (funcall setter `(+ ,getter ,(or delta 1)))))
+
+;;;###autoload
+(defmacro decf (place &optional delta)
+  "Decrement PLACE by DELTA (default to 1).
+
+The DELTA is first subtracted from PLACE, and then stored in PLACE.
+Return the decremented value of PLACE.
+
+See also `incf'."
+  (declare (debug (gv-place &optional form)))
+  (gv-letplace (getter setter) place
+    (funcall setter `(- ,getter ,(or delta 1)))))
 
 ;; For Edebug, the idea is to let Edebug instrument gv-places just like it does
 ;; for normal expressions, and then give it a gv-expander to DTRT.
index 23b9b582a9a6e7620f078afe1200a8adc6e3bb1c..77a4ec4f21c951e7104fff6d4ddd15c0a88ed1eb 100644 (file)
@@ -1375,9 +1375,17 @@ A FUNC form can have any number of `:no-eval' (or `:no-value'),
    :eval (mod 10 6)
    :eval (mod 10.5 6))
   (1+
-   :eval (1+ 2))
+   :eval (1+ 2)
+   :eval (let ((x 2)) (1+ x) x))
   (1-
-   :eval (1- 4))
+   :eval (1- 4)
+   :eval (let ((x 4)) (1- x) x))
+  (incf
+   :eval (let ((x 2)) (incf x) x)
+   :eval (let ((x 2)) (incf x 2) x))
+  (decf
+   :eval (let ((x 4)) (decf x) x)
+   :eval (let ((x 4)) (decf x 2)) x)
   "Predicates"
   (=
    :args (number &rest numbers)
index 5baa155c592c001cee64377592eaa2b20ab98c02..5fbfbb7899ed53c6ff8c4e9e12fb5464154d86cb 100644 (file)
                values-list
                values
                pushnew
-               decf
-               incf
                ))
   (let ((new (if (consp fun) (prog1 (cdr fun) (setq fun (car fun)))
                (intern (format "cl-%s" fun)))))
index 376566958a01d59fc86921710a6761759473c842..d7c38b734325e78cce8a808e14e4f498a7070e85 100644 (file)
   (should (equal (cl-multiple-value-list nil) nil))
   (should (equal (cl-multiple-value-list (list 1 2 3)) '(1 2 3))))
 
-(defvar cl-lib-test--special 0)
-
-(ert-deftest cl-lib-test-incf ()
-  (setq cl-lib-test--special 0)
-  (should (= (cl-incf cl-lib-test--special) 1))
-  (should (= cl-lib-test--special 1))
-  (should (= (cl-incf cl-lib-test--special 9) 10))
-  (should (= cl-lib-test--special 10))
-  (let ((var 0))
-    (should (= (cl-incf var) 1))
-    (should (= var 1))
-    (should (= (cl-incf var 9) 10))
-    (should (= var 10)))
-  (let ((alist))
-    (should (= (cl-incf (alist-get 'a alist 0)) 1))
-    (should (= (alist-get 'a alist 0) 1))
-    (should (= (cl-incf (alist-get 'a alist 0) 9) 10))
-    (should (= (alist-get 'a alist 0) 10))))
-
-(ert-deftest cl-lib-test-decf ()
-  (setq cl-lib-test--special 0)
-  (should (= (cl-decf cl-lib-test--special) -1))
-  (should (= cl-lib-test--special -1))
-  (should (= (cl-decf cl-lib-test--special 9) -10))
-  (should (= cl-lib-test--special -10))
-  (let ((var 1))
-    (should (= (cl-decf var) 0))
-    (should (= var 0))
-    (should (= (cl-decf var 10) -10))
-    (should (= var -10)))
-  (let ((alist))
-    (should (= (cl-decf (alist-get 'a alist 0)) -1))
-    (should (= (alist-get 'a alist 0) -1))
-    (should (= (cl-decf (alist-get 'a alist 0) 9) -10))
-    (should (= (alist-get 'a alist 0) -10))))
-
 (ert-deftest cl-digit-char-p ()
   (should (eql 3 (cl-digit-char-p ?3)))
   (should (eql 10 (cl-digit-char-p ?a 11)))
index 5ea386e0b5d85ea5a683f7b7fcab08f83b844edc..892af4bfab110b940a0e08aa5687cc87b4e137a7 100644 (file)
@@ -163,6 +163,42 @@ its getter (Bug#41853)."
         (eval-buffer))))
   (should (equal (get 'gv-setter-edebug 'gv-setter-edebug-prop) '(123))))
 
+(defvar gv-test--special 0)
+
+(ert-deftest gv-incf ()
+  (setq gv-test--special 0)
+  (should (= (incf gv-test--special) 1))
+  (should (= gv-test--special 1))
+  (should (= (incf gv-test--special 9) 10))
+  (should (= gv-test--special 10))
+  (let ((var 0))
+    (should (= (incf var) 1))
+    (should (= var 1))
+    (should (= (incf var 9) 10))
+    (should (= var 10)))
+  (let ((alist))
+    (should (= (incf (alist-get 'a alist 0)) 1))
+    (should (= (alist-get 'a alist 0) 1))
+    (should (= (incf (alist-get 'a alist 0) 9) 10))
+    (should (= (alist-get 'a alist 0) 10))))
+
+(ert-deftest gv-decf ()
+  (setq gv-test--special 0)
+  (should (= (decf gv-test--special) -1))
+  (should (= gv-test--special -1))
+  (should (= (decf gv-test--special 9) -10))
+  (should (= gv-test--special -10))
+  (let ((var 1))
+    (should (= (decf var) 0))
+    (should (= var 0))
+    (should (= (decf var 10) -10))
+    (should (= var -10)))
+  (let ((alist))
+    (should (= (decf (alist-get 'a alist 0)) -1))
+    (should (= (alist-get 'a alist 0) -1))
+    (should (= (decf (alist-get 'a alist 0) 9) -10))
+    (should (= (alist-get 'a alist 0) -10))))
+
 (ert-deftest gv-plist-get ()
   ;; Simple `setf' usage for `plist-get'.
   (let ((target (list :a "a" :b "b" :c "c")))