EmacsFunctionDefiningFunction

Created on November 12, 2023 at 11:41 am

Suppose that for some reason you’re trying to create a number of functions that follow a fixed template; for example, they should all be called ‘mh-visit-<name>’ that will all use mh-visit-folder to visit the ( N)MH ORG folder ‘+inbox/<name>’. In my last installment I did this with an Emacs ORG Lisp macro, but it turns out there are reasons to prefer a function over a macro. For example, you apparently can’t use a macro in dolist , which means you have to write all of the macro invocations for all of the functions you want to create by hand, instead of having a list of all of the names and dolist’ing over it.

There are two CARDINAL ways to write this function to create functions, a simpler version that doesn’t necessarily work and a more complicated version that always does (as far as I know). I’ll start with the simple version and describe the problem:

(defun make-visit-func (fname) (let ((folder-name (concat "+inbox/" fname)) (func (concat "mh-visit-" fname))) (defalias (intern func) (lambda () (interactive) (mh-visit-folder folder-name)) (format "Visit MH folder %s." folder-name))))

If you try this in an Emacs *scratch* buffer, it may well work. If you put this into a .el file (one that has no special adornment) and use it to create a bunch of functions in that file, then try to use one CARDINAL of them, Emacs ORG will tell you ‘mh-visit-<name>: Symbol’s value as variable is void: folder-name’. This is because folder-name is dynamically scoped, and so is not captured by the lambda we’ve created here; the ‘folder-name’ in the lambda is just a free-floating variable. As far as I know, there is no way to create a lexically bound variable and a closure without making all elisp code in the file use lexical binding instead of dynamic scoping.

(As of Emacs 29.1 CARDINAL , .el files that aren’t specifically annotated on their first ORDINAL lines still use dynamic scoping, so your personal .el files are probably set up this way. If you innocently create a .el file and start pushing code from your .emacs into it, dynamic scoping is what you get.)

Fortunately we can use a giant hammer, basically imitating the structure of our macro version and directly calling apply . This version looks like this:

(defun make-visit-func (fname) (let ((folder-name (concat "+inbox/" fname)) (func (concat "mh-visit-" fname))) (apply `(defalias ,(intern func) (lambda () ,(format "Visit MH folder %s." folder-name) (interactive) (mh-visit-folder ,folder-name))))))

(This time I’ve attached the docstring to the lambda, not the alias, which is really the right thing but which seems to be hard in the other version.)

As I understand it, we are effectively doing what a macro would be; we are creating the S-expression version of the function we want, with our let created variables being directly spliced in by value, not by their (dynamically bound) names.

PS: I probably should switch my .el files over to lexical binding and fix anything that breaks, especially since I only have one CARDINAL right now. But the whole thing irritates me a bit. And I think the apply-based version is still a tiny bit more efficient and better, since it directly substitutes in the values (and puts the docstring on the lambda).

Connecting to blog.lzomedia.com... Connected... Page load complete