3

I want to define a bundle of variable-and-function pairs, e.g.: vl/path-doc points to my often used path, and vl/open-path-doc is used for open it in dired-mode.

I tried this piece of code:

(setq vivo-work-directory "~/Workspace/vivo/")

(defvar vl/paths
  '((aktualoj . "doc/aktualoj/")
    (doc . "doc/")
    (analysis . "analysis/")
    (lernejo . "lernejo/")
    (utils . "utils/")
    (topics . "vivo/topics/")
    (projects . "vivo/projects/")))

(defun vl/create-path-funcs (p-list)
  (mapc
   (lambda (x)
     (let* ((s (car x))
            (p (concat vivo-work-directory (cdr x)))
            (sym-p (make-symbol (concat "vl/path-" (symbol-name s))))
            (sym-f (make-symbol (concat "vl/open-path-" (symbol-name s)))))
       (progn
         (set sym-p p)
         (set sym-f (lambda () (interactive) (dired p))))
       ))
   p-list))

(vl/create-path-funcs vl/paths)

Then, I hope I can use M-x to invoke vl/path-doc, vl/open-path-doc, etc. But I failed.

Of course, I can define these pairs one by one. But if it's OK, I can change my vl/paths without re-defining anything else. So, if possible, how to define the pairs of variable-and-function based on a list?

Drew
  • 75,699
  • 9
  • 109
  • 225
Vivodo
  • 133
  • 5

2 Answers2

5

make-symbol returns an uninterned symbol. That means you get a symbol with name, function cell, value cell, and property list but the symbol is not registered in the global obarray. Therefore, you cannot use it for function evaluation with the usual parenthesis notation.

If you replace make-symbol by intern the symbol is also registered in the obarray and you can use it in the usual way.

Furthermore, you cannot use set to set the symbol's function cell. You must use fset instead.

Last but not least I think you want the local variable p in the lambda expression be replaced by the value of sym-p which is itself a symbol with a name prefix vl/path-. You can use back-quote notation for that purpose.

(setq vivo-work-directory "~/Workspace/vivo/")

(defvar vl/paths
  '((aktualoj . "doc/aktualoj/")
    (doc . "doc/")
    (analysis . "analysis/")
    (lernejo . "lernejo/")
    (utils . "utils/")
    (topics . "vivo/topics/")
    (projects . "vivo/projects/")))

(defun vl/create-path-funcs (p-list)
  (mapc
   (lambda (x)
     (let* ((s (car x))
            (p (concat vivo-work-directory (cdr x)))
            (sym-p (intern (concat "vl/path-" (symbol-name s))))
            (sym-f (intern (concat "vl/open-path-" (symbol-name s)))))
         (set sym-p p)
         (fset sym-f `(lambda () (interactive) (dired ,sym-p))))
       )
   p-list))

(vl/create-path-funcs vl/paths)
Tobias
  • 32,569
  • 1
  • 34
  • 75
  • Note that writing `progn` within `let*` is redundant and that, under `lexical-binding`, the inner-most `lambda` does not need to be backquoted (and it is always better not to quote anonymous functions), as `sym-p` will be lexically captured by the closure. – Basil Dec 27 '17 at 18:26
  • I removed the `progn`. It was just a left-over from the copy of the OP's version. It just didn't hurt. I think `lexical-binding` would really make a new answer. But, in that case the explanation would be a bit longer. – Tobias Dec 27 '17 at 19:00
1

@Tobias nice. Here still a command making new entries:

(defun vl/new-pathsfunc ()
  (interactive)
  (let ((directory (read-from-minibuffer "directory: " (substring default-directory 2)))
    (name (intern (read-from-minibuffer "Name: "))))
    (unless (map-contains-key vl/paths name)
      (push (cons name directory) vl/paths)
      (vl/create-path-funcs vl/paths))))
Andreas Röhler
  • 1,894
  • 10
  • 10