Fork me on GitHub
#emacs
<
2023-04-09
>
Kimo17:04:06

An elisp alist with duplicate keys is valid. Even the https://www.gnu.org/software/emacs/manual/html_node/elisp/Association-Lists.html mention the use of duplicate keys: > Association lists are often used to record information that you might otherwise keep on a stack... Of course, a Clojure map doesn't play that game. However, https://github.com/clojure-emacs/parseedn produces invalid edn in this case:

(parseedn-print-str '((:x 1) (:x 2)))
;; "{:x (1), :x (1)}"
Not sure what the best solution would be. Deleting keys wouldn't be nice, nor would an incidental map-or-list. Is there a workaround I haven't considered? Maybe alists just shouldn't be maps?

dpsutton18:04:35

This seems a straightforward bug? Alists are ordered and earlier keys shadow later keys right?

dpsutton18:04:31

That alist, as an associative structure, has only a single key. The map it was printed as is invalid. Seems just a bug

Kimo18:04:24

> earlier keys shadow later keys That's true as far as the alist interface is concerned. The later keys still have meaning, though, and they're accessible via the ordinary list. In my use-case, I receive a list of lists from org-element-parse-buffer. I need all of them, and I don't care if the list is associative.

Kimo19:04:27

I'd prefer to get:

(parseedn-print-str '((:x 1) (:x 2)))
;; "((:x 1) (:x 2))"

Kimo19:04:39

Or, at least, some representation of the later keys. You're right, though, "{:x (1), :x (1)}" is definitely a bug. Nobody needs the top of the stack twice in an invalid map.

dpsutton19:04:56

if you turn the alist into a vector i think you’ll get what you want

Kimo19:04:13

Oh, that's great. I didn't even realize emacs had vectors, haha.

dpsutton19:04:16

actually it seems like it treats all tuples as maps. (parseedn-print (vector (vector ':x 1) (vector ':x 2)))

dpsutton19:04:45

so you’ll have to munge the data a bit but that’s fair as you are taking an associate item and treating it like a seq

dpsutton19:04:27

and to figure all this out i’m just looking at the source of parseedn-print. and the vectorp clause is easy to see

Kimo19:04:27

Cool, I'm on the way to a good workaround, with something like:

(parseedn-print-str (vconcat [] '((:x 1) (:x 2))))
;; "[{:x 1} {:x 2}]"
Thanks for thinking through it with me.

dpsutton19:04:18

I bet you want all of the cons cells to be vectors instead of single key maps but maybe it doesn’t matter

Kimo19:04:38

Yes, if I can wrestle elisp into it, haha

Kimo19:04:49

If the parseedn functions had an option to ignore associative constructs entirely, that could be a reasonable solution.

👀 1
Kimo19:04:57

(defun list->vec (l)
  (if (not (listp l)) l
    (vconcat [] l)))

(parseedn-print-str
 (list->vec (mapcar #'list->vec '((:x 1) (:x 2)))))
;; "[[:x 1] [:x 2]]"

Drew Verlee19:04:23

I'm building an emacs function that will display the keys in a map so you can select it. The attached image might help give you an idea of what this feels like. Here is the code so far, suggestions welcome. For instance, thinking I can probably swap the helm stuff out for the more generic function completing-read-multiple

(defun my-multi-select-function (callback keys)
  (interactive)
  (helm :sources (helm-build-sync-source "Choose items"
                   :candidates keys
                   :action (lambda (candidate)
                             (funcall callback (helm-marked-candidates))))
        :buffer "*helm multi-select*"
        :candidate-number-limit 9999))

(defun insert-selected-keys (selected-items)
  ;; (insert (mapconcat 'identity selected-items ", "))
  (insert
   (if (equal 1 (length selected-items))
       (car selected-items)
     (format-list selected-items)
     )))

(defun clojure-keys ()
  (interactive)
  ;; call keys on the map and get the result
  (cider-interactive-eval "(keys *1)")
  ;; hacky sleep to avoid register not being ready
  (sit-for 0.25)
  (my-multi-select-function #'insert-selected-keys (get-keys-from-cider-regsiter)))

(defun get-keys-from-cider-regsiter ()
  (mapcar (lambda (k) (format "%s" k)) (car (read-from-string (get-register ?e)))))

(global-set-key (kbd "C-c s") #'clojure-keys)

(defun format-list (lst)
  "Takes a list and returns a formatted string.
   If the list has only one item, returns the item.
   If the list has multiple items, returns the list inside a string with the 'select-keys' prefix."
  (if (= (length lst) 1)
      (car lst)
    (format "(select-keys [%s])" (mapconcat 'identity lst " "))))

Benjamin C19:04:30

Hey that's a really cool idea, I like it!