Is there a more ergonomic way to apply a function to a field in a struct in Elisp?
Say I have the following:
(cl-defstruct stack xs)
(defvar stack (make-stack :xs '(1 2 3 4 5)))
Is there an easy way to apply functions to the :xs
field. I'd like an API like this:
(update-field :xs stack (lambda (xs)
(cl-map 'list #'1+ '(1 2 3 4 5))))
Does anyone know if this exists?
Update:
I'm looking for a way to DRY up the calls to (stack-xs stack)
(see below). What I'm looking for is more similar to something like Map.update
from Elixir.
(setf (stack-xs stack) (cl-map 'list #'1+ (stack-xs stack)))
I ended up solving this by writing a macro, struct/update
:
(defmacro struct/update (type field f xs)
"Apply F to FIELD in XS, which is a struct of TYPE.
This is immutable."
(let ((copier (->> type
symbol-name
(string/prepend "copy-")
intern))
(accessor (->> field
symbol-name
(string/prepend (string/concat (symbol-name type) "-"))
intern)))
`(let ((copy (,copier ,xs)))
(setf (,accessor copy) (funcall ,f (,accessor copy)))
copy)))
I use this macro as such:
(defun set/add (x xs)
"Add X to set XS."
(struct/update set
xs
(lambda (table)
(let ((table-copy (ht-copy table)))
(ht-set table-copy x 10)
table-copy))
xs))
Which will update the following struct:
(cl-defstruct set xs)