1

So I'm trying to process the following nested list...

(setq *test*
  '("mt" "trello"
    ("b" "board"
     ("l" org-trello-show-board-labels)
     ("d" org-trello-sync-buffer-down)
     ("u" org-trello-sync-buffer-up)
     ("s" "setup"
      ("c" org-trello-create-board-and-install-metadata)
      ("I" org-trello-install-board-metadata)
      ("u" org-trello-update-board-metadata)))
    ("c" "card"
     ("a" org-trello-assign-me)
     ("A" org-trello-unassign-me)
     ("x" org-trello-archive-card)
     ("X" org-trello-archive-cards)
     ("c" "comment"
      ("c" org-trello-add-card-comment)
      ("C" org-trello-delete-card-comment))
     ("k" "kill"
      ("c" org-trello-kill-cards)
      ("e" org-trello-kill-entity)))
    ("d" "from trello"
     ("b" org-trello-sync-buffer-down)
     ("c" org-trello-sync-card-down))
    ("g" org-trello-abort-sync)
    ("h" "help"
     ("k" org-trello-help-describing-bindings)
     ("v" org-trello-version))
    ("j" "jump"
     ("b" org-trello-jump-to-trello-board)
     ("c" org-trello-jump-to-trello-card))
    ("s" "setup"
     ("I" org-trello-install-key-and-token)
     ("c" org-trello-check-setup))
    ("u" "to trello"
     ("b" org-trello-sync-buffer-up)
     ("c" org-trello-sync-card-up))))

Using this recursive function:

(defun *test/pcase-test (prefix &rest tree)
  (pcase tree
    (`(,(and (pred consp) items))
     (pcase items
       (`(,(and (pred stringp) key)
          ,(and (pred stringp) label)
          .
          ,children)
        (push `(,*prefix-func 'org-mode ,(concat "m" prefix key) ,label) *result*)
        (apply #'*test/pcase-test (concat prefix key) children))
       (`(,(and (or (pred stringp) (pred symbolp)) key)
          ,(and (pred symbolp) fn))
        (push `(,*leader-key-func 'org-mode ,(concat prefix key) ',fn) *result*))))
    (_
     (error "Not a valid definition:\n%s" (pp-to-string tree)))))

NB: I usually begin this function by setting *result* as nil, and setting *prefix-func and *leader-key-func to specific function symbols. I plan to use nreverse to process it's final form.

The goal is to transform this nested list into a series of function calls, *prefix-func for nested lists beginning with two string items (while passing the subsequent items, i.e. children, into another recursive function call, and *leader-key-func for two-item lists consisting of a string (or a symbol) followed by a symbol (which could probably be fboundp, but I haven't decided on that yet).

What's mystifying me is that, when I instrument this function and step through it, it run through the (,(and (pred consp) items) once, and then (since it runs the second nested pcase) it will match (,(and (predp stringp) key)..., pushing at least one item into *result*. But when I step through the second round of execution, despite the fact that when I type e and evaluate (consp tree) (which returns t), it doesn't match (,(and (pred consp) items) the second time.

I definitely don't understand what's happening, or even if I'm doing the right thing with pcase (I've gone through this reference, this EmacsWiki entry, and even the original Emacs reference on pattern matching case, which I admit I don't fully grok. I think I'm doing the right thing, but there's a nuance here I'm not fully getting.

I note that if I replace the (_...) with the following code, it works, but I would like it if the function to throw an error if it comes up with a nested list it doesn't understand:

(items
  (dolist (item items)
    (apply #'*test/pcase-test prefix item))))

I know for a fact that the above code will break if item isn't a list, and I would rather that the function I'm using would just signal me early, rather than break further down the line. Plus, this feels like a cop-out, because then I'd just make a pattern-case that matches anything and hack a way out of my predicament, which still means I don't understand how pcase works.

What am I doing wrong? Anyone's insight on this would be greatly appreciated, thank you!

Drew
  • 75,699
  • 9
  • 109
  • 225
Tariq Kamal
  • 390
  • 1
  • 9
  • It might help if you pared down the example to just what's necessary to pose your question. IOW, isolate the specific problem more. Just a suggestion. – Drew Nov 06 '18 at 18:27

1 Answers1

1
(defun *test/pcase-test (prefix &rest tree)
  (pcase tree
    (`(,(and (pred consp) items))

[...] But when I step through the second round of execution, despite the fact that when I type e and evaluate (consp tree) (which returns t), it doesn't match (,(and (pred consp) items) the second time.

That pcase clause you cite is actually equivalent to (and (= (length tree) 1) (consp (car tree))). To see this, simplify the pattern by ignoring the pred:

(pcase tree
  (`(,items))...)

The above checks that tree is a list of one element (and binds items to that element). The pred that you added checks items, not tree.


What am I doing wrong?

I'm guessing you make your initial call like this:

(let ((*result* nil))
  (*test/pcase-test "PREFIX" *test*)
  *result*)

In this case tree is a list containing single "definition" list, so the top-level call is okay. Then for the recursive call, you passing a list of several "definition" lists:

(apply #'*test/pcase-test (concat prefix key) children)

In this case tree is a list which looks like (("b" "board" ...) ("c" "card" ...) ("k" "kill" ...) ...). You have nothing in your code to handle this multiple children case, so it doesn't work.

npostavs
  • 9,033
  • 1
  • 21
  • 53