2015-09-12 A trick with argument for interactive call

Today’s post will be quite technical, but it might be useful for someone, so I’m sharing what I have learned some time ago.

Assume that you are writing a command which should take two parameters. When called interactively, it should ask for both of them in the minibuffer. However (and here the tricky part comes), you want the prompt for the second one to include the first one. (If you can’t see any possible use for such behavior, try M-x copy-file. That function, however, is written in C, not in Elisp, so studying its source code probably won’t help us too much.)

Of course, this won’t work:

(defun my-command-broken (arg1 arg2)
  (interactive (list
		(read-string "First arg: ")
		(read-string (format "First arg was %s. Second arg: " arg1))))
  (message "First arg was %s, second arg was %s." arg1 arg2))

The reason is clear: arg1 and arg2 are bound at the end of the interactive call, so when the prompt for the second argument is needed, arg1 is not yet bound.

Well, it is quite similar to the let=/=let* distinction. If only there were something like interactive*

Well, there isn’t, but it turns out that it’s easy to simulate. Look at this:

(defun my-command (arg1 arg2)
  (interactive (let* ((arg1 (read-string "First arg: "))
		      (arg2 (read-string (format "First arg was %s. Second arg: " arg1))))
		 (list arg1 arg2)))
  (message "First arg was %s, second arg was %s." arg1 arg2))

The let* form does its work of binding arg1 before the prompt for arg2 is computed. Then, we just construct the list of the two just-read arguments. (Notice also that I chose to use the same names within the let form as in the defun. It’s ok, though not necessary – they may be something completely different, say, spam and larch;-).) The point is that the form right after interactive should return a list of two elements, and that is everything interactive cares for – what the (local) variables inside let* are called is none if its business. The reason I chose the very same names is convenience and readability, though YMMV.

Now I’m quite sure that you could cook up a macro interactive*, which would wrap all this in a nice syntax. That, though, I will try some other day.

CategoryEnglish, CategoryBlog, CategoryEmacs