2
(setq ali '())
>>> nil
(cl-incf (alist-get 'a ali 0))
>>> ((a . 1))
(setf (alist-get 'b ali) 0)
>>> ((b . 0) (a . 1))
(cl-incf (alist-get 'b ali 0))
>>> 1

As you can see, cl-incf returns different kinds of values in the second and the fourth expressions.

cl-incf per documentation is supposed to return "the incremented value of PLACE", however as we can see with the key 'a it returns the whole ali structure. If we macroexpand it a few times, it's clear what's happening:

(let*
    ((p
      (if
          (and nil
               (not
                (eq nil 'eq)))
          (assoc 'a ali nil)
        (assq 'a ali)))
     (v
      (+
       (if p
           (cdr p)
         0)
       1)))
  (if p
      (setcdr p v)
    (setq ali
          (cons
           (setq p
                 (cons 'a v))
           ali))))

The last setq is applied to ali, so it returns the new value of ali.

It seems to be a bug in the alist generalized variable definition.

Am I correct? If so, how to report it? If not, what's the idiomatic way to work around it, preferably not calling alist-get two times?

  • Yep, the behavior for the second sexp, (cl-incf (alist-get 'a ali 0)) looks wrong. Consider filing a bug report: M-x report-emacs-bug. – Drew Aug 12 at 20:29
  • I think the last part of the expansion should be something like this: (if p (setcdr p v) (setq ali (cons (setq p (cons 'a v)) ali)) v). IOW, after updating ali it should return v, not ali. In all cases it should return v. – Drew Aug 12 at 20:41
  • I went ahead and filed bug report #42837 for this. Wasn't sure you'd do it, and wanted to get it done. Feel free to chime in there. – Drew Aug 12 at 22:57
  • Emacs 27.1's return value is correct, in other words, for your seconds expression, it returns 1 instead of ((a . 1)). I don't have Emacs 26.3 installed, so I can't test that version. – xuchunyang Aug 13 at 3:45
  • @Drew thank you for filing the report, it's okay that you did it. – grepcake Aug 13 at 8:42
2

Yep, the behavior for the second sexp, (cl-incf (alist-get 'a ali 0)) looks wrong. I think the last part of the expansion should be something like this:

(if p
    (setcdr p v)
  (setq ali (cons (setq p (cons 'a v)) ali))
  v)

IOW, after updating ali it should return v, not ali. In all cases it should return v.

I filed Emacs bug #42837 for this. In the future, you can do it yourself via report-emacs-bug.

But it turns out this has already been fixed in Emacs 27.1, which has just been released.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.