6

In my .emacs file I have code like this:

(setq default-frame-alist
      (assq-delete-all 'width default-frame-alist))

(add-to-list 'default-frame-alist
             (cons 'width new-width))

The first expression ensures that the second one does not introduce a "degenerate key" in the alist (IOW, multiple elements having the same car).

That's a javaesque amount of machinery for what other languages achieve with something that looks very similar to this:

default_frame_list["width"] = new_width

Is there a simpler way?

PS: Of course, one could reply with "just get rid of the first expression" (IOW, "don't worry about degenerate keys"), but this does not answer my question.

  • 3
    Possible duplicate of How to replace an element of an alist?. Short version: use setf if you want to update the list in place, but the idiomatic way is to push a new value onto the front of the list. That new value shadows the later values, which are effectively ignored. – Dan Feb 18 '15 at 1:15
10

In Emacs-25, there's alist-get which you can use to get and set a key. E.g. you can do what you want with:

(setf (alist-get 'width default-frame-alist) new-width)
  • 1
    If it's the same alist-get given here, I don't see where the key is set... – kjo Feb 18 '15 at 18:41
  • 3
    The setter is in gv.el. – Stefan Feb 19 '15 at 1:41
  • 2
    Unfortunately, this doesn't work if your alist keys are strings. For a solution that does, see emacs.stackexchange.com/a/33893/12534. – Radon Rosborough Jul 1 '17 at 4:09
  • 1
    Indeed, I'm beginning to think it was a mistake to use assq instead of assoc in the definition of alist-get. – Stefan Jul 1 '17 at 4:33
4

First off, if you think that your alist has duplicate keys (which you call 'degenerate') and you want to remove them then you will have to call assq-delete-all or something like that.

As to how to set the value without creating duplicate keys (since the simplest way to deal with an alist is to just cons on a new cons pair to the front) you will need to use something like setf.

There are a few functions you can use, they are all similar and effectively do the same thing. They set the value of a place. A place is something like a car or a cdr of a list.

You can use setcdr (or its alias rplacd) to set the value of the cdr of the cons cell returned by assq to the new value:

(rplacd (assq 'width default-frame-alist) new-width)

Or you can use setf to do the same thing:

(setf (cdr (assq 'width default-frame-alist) new-width)

setf is more general and works by looking up setf methods defined for the place (i.e. car or cdr or first) and then calling that function with the other parameters. There are even ways for one to define one's own setf methods.

For reference look up Generalized Variables in the Elisp manual.

  • (defun update-alist-tag (tag val alist) (nconc (list (cons tag val)) (assq-delete-all tag alist))) – James Youngman Feb 17 '15 at 23:11
  • That could work as well. I currently prefer the setf form myself. – verdammelt Feb 17 '15 at 23:13
  • 2
    This doesn't work if alist doesn't contain the key already. – politza Feb 18 '15 at 11:42
  • @politza ouch! you are correct. I'll have to dig into the manual to see if there is a consise way around that. I think though the idiom of consing a new pair onto the alist is the way to do it - but outside of the question at hand. – verdammelt Feb 18 '15 at 11:49
1

There is an old alist.el library from the Emacs vs XEmacs days - someone added it to their .emacs folder so you can browse it - https://github.com/baron/emacs/blob/master/.emacs.d/el-get/apel/alist.el. It would let you say

(set-alist 'default-frame-alist 'width 80)

It's not namespaced to 'alist-' though, and not orthogonal - eg it has (put-alist KEY VALUE ALIST) vs (set-alist SYMBOL KEY VALUE), so it could use a little updating.

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.