New macros incf and decf

* 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.
This commit is contained in:
Stefan Kangas 2025-02-22 17:32:31 +01:00
parent 44a1c4a9ae
commit 95fee880e4
10 changed files with 135 additions and 161 deletions

View 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.

View 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

View 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

View file

@ -491,12 +491,18 @@ Emacs 25.1), and gnudoit (obsolete since Emacs 25.1).
** CL-Lib
+++
*** Some cl-lib functions are now built-in.
The functions 'cl-plusp', 'cl-minusp', 'cl-oddp', and 'cl-evenp', have
been added to Emacs Lisp, and are thus now aliases for the built-in
functions 'plusp', 'minusp', 'oddp' and 'evenp'. The old names are
considered deprecated, and will be marked as obsolete in some future
release.
*** Some cl-lib functions and macros are now built-in.
These functions or macros have been added to Emacs Lisp, and the old
names are now aliases for the built-in equivalents:
- 'cl-incf' renamed to 'incf'
- 'cl-decf' renamed to 'decf'
- 'cl-oddp' renamed to 'oddp'
- 'cl-evenp' renamed to 'evenp'
- 'cl-plusp' renamed to 'plusp'
- 'cl-minusp' renamed to 'minusp'
The old names are considered deprecated, and will be marked as obsolete
in some future release.
+++
*** 'cl-labels' now also accepts '(FUNC EXP)' bindings, like 'cl-flet'.
@ -1356,6 +1362,11 @@ change it globally with:
(set-default-toplevel-value 'lexical-binding t)
+++
*** New functions 'incf' and 'decf'.
They increment or decrement the value stored in a variable (a symbol),
or in a generalized variable.
+++
** New functions 'plusp' and 'minusp'.
They return non-nil if a number is positive or negative, respectively,

View 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.
(defmacro cl-decf (place &optional x)
This macro is considered deprecated in favor of the built-in macro
`incf' that was added in Emacs 31.1.")
(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.

View 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.

View 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)

View file

@ -282,8 +282,6 @@
values-list
values
pushnew
decf
incf
))
(let ((new (if (consp fun) (prog1 (cdr fun) (setq fun (car fun)))
(intern (format "cl-%s" fun)))))

View file

@ -63,42 +63,6 @@
(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)))

View 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")))