9
votes

I'm trying to write a macro in emacs lisp to create some ‘helper functions.’

Ultimately, my helper functions will be more useful than what I have here. I realize that there may be better/more intuitive ways to accomplish the same thing (please post) but my basic question is why won't this work/what am I doing wrong:

(defmacro deftext (functionname texttoinsert)
  `(defun ,(make-symbol (concatenate 'string "text-" functionname)) ()
     (interactive)
     (insert-string ,texttoinsert)))

(deftext "swallow" "What is the flight speed velocity of a laden swallow?")
(deftext "ni" "What is the flight speed velocity of a laden swallow?")

If I take the output of the macroexpand and evaluate that, I get the interactive functions I was intending to get with the macro, but even though the macro runs and appears to evaluate, I can't call M-x text-ni or text-swallow.

4

4 Answers

12
votes

This does what you want:

(defmacro deftext (functionname texttoinsert)
  (let ((funsymbol (intern (concat "text-" functionname))))
`(defun ,funsymbol () (interactive) (insert-string ,texttoinsert))))
4
votes

As has been pointed out, the solution is to use intern instead of make-symbol.

It's possible to create multiple independent symbols with the same name, but only one of them can be the canonical symbol for the given name -- i.e. the symbol you will obtain when you refer to it elsewhere.

intern returns the canonical symbol for the given name. It creates a new symbol only if no interned symbol by that name already exists. This means that it will only ever create one symbol for any given name1.

make-symbol, on the other hand, creates a new symbol every time it is called. These are uninterned symbols -- each one is a completely valid and functional symbol, but not the one which will be seen when you refer to a symbol by its name.

See: C-hig (elisp) Creating Symbols RET

Because defun returns the symbol it sets, you can observe what's going on by capturing the return value and using it as a function:

(defalias 'foo (deftext "ni" "What is the flight speed velocity of a laden swallow?"))
M-x text-ni ;; doesn't work
M-x foo ;; works

or similarly:

(call-interactively (deftext "shrubbery" "It is a good shrubbery. I like the laurels particularly."))

The tricky part in all this -- and the reason that evaluating the expanded form did what you wanted, and yet the macro didn't -- is to do with exactly how and when (or indeed if) the lisp reader translates the name of the function into a symbol.

If we write a function foo1:

(defun foo1 (texttoinsert) (insert-string texttoinsert))

The lisp reader reads that as text, and converts it into a lisp object. We can use read-from-string to do the same thing, and we can look at the printed representation of the resulting lisp object:

ELISP> (car (read-from-string "(defun foo1 (texttoinsert) (insert-string texttoinsert))"))
(defun foo1
    (texttoinsert)
  (insert-string texttoinsert))

Within that object, the function name is the canonical symbol with the name "foo1". Note, however, that the return value we see from read-from-string is only the printed representation of that object, and the canonical symbol is represented only by its name. The printed representation does not enable us to distinguish between interned and uninterned symbols, as all symbols are represented only by their name.

(Skipping ahead momentarily, this is the source of your issue when evaluating the printed expansion of your macro, as that printed form is passed through the lisp reader, and what was once an uninterned symbol becomes an interned symbol.)

If we then move on to macros:

(defmacro deffoo2 ()
  `(defun foo2 (texttoinsert) (insert-string texttoinsert)))

ELISP> (car (read-from-string "(defmacro deffoo2 ()
        `(defun foo2 (texttoinsert) (insert-string texttoinsert)))"))
(defmacro deffoo2 nil
  `(defun foo2
       (texttoinsert)
     (insert-string texttoinsert)))

This time the reader has read the macro definition into a lisp object, and within that object is the canonical symbol foo2. We can verify this by inspecting the object directly:

ELISP> (eq 'foo2
           (cadr (cadr (nth 3
            (car (read-from-string "(defmacro deffoo2 ()
             `(defun foo2 () (insert-string texttoinsert)))"))))))
t

So for this macro, it is already dealing with that canonical symbol foo2 before any macro call/expansion happens, because the lisp reader established that when reading the macro itself. Unlike our previous simple function definition (in which the function symbol was determined by the lisp reader as the function was defined), when a macro is called & expanded the lisp reader is not utilised. The macro expansion is performed using pre-existing lisp objects -- no reading is necessary.

In this example the function symbol is already present in the macro, and because it is the canonical symbol we could use (foo2) elsewhere in our code to call that function. (Or, had I made the definition interactive, use M-x foo2.)

Finally getting back to the original macro from the question, it's obvious that the lisp reader never encounters a function name symbol for the function it will define:

ELISP> (car (read-from-string "(defmacro deftext (functionname texttoinsert)
        `(defun ,(make-symbol (concatenate 'string \"text-\" functionname)) ()
           (interactive)
           (insert-string ,texttoinsert)))"))
(defmacro deftext
    (functionname texttoinsert)
  `(defun ,(make-symbol
            (concatenate 'string "text-" functionname))
       nil
     (interactive)
     (insert-string ,texttoinsert)))

Instead this object produced by the lisp reader contains the expression ,(make-symbol (concatenate 'string "text-" functionname)); and that backquoted expression will be evaluated at expansion time to create a new uninterned symbol which will be a part of the object created by that expansion.

In our earlier examples the resulting object had a car of defun (interned), and a cadr of foo1 or foo2 (both also interned).

In this last example, the object has a car of defun (interned) but a cadr of an uninterned symbol (with the name resulting from the concatenate expression).

And finally, if you print that object, the printed representation of that uninterned function symbol will be the symbol name, and reading that printed representation back by evaluating it would cause the function cell for the canonical symbol to be defined instead.

1 In fact the unintern function can be used to unintern a symbol, after which calling intern for the same name would naturally create a new symbol; but that's not important for this discussion.

3
votes

FWIW, if you use lexical-binding, you don't need to use a macro:

(defun deftext (functionname texttoinsert)
  (defalias (intern (concat "text-" functionname))
    (lambda ()
      (interactive)
      (insert-string texttoinsert))))
1
votes

It's been years, but I think you're probably missing an fset to define the function; see the docs if you are wanting it a compile time too.