3

I would have thought that both of the funcall's the follow would have yielded the same result, but they don't and I'm trying to understand why.

(defmacro test/z () "z")
(funcall (function test/z)) ;; => "z"
(funcall (eval `(function ,(intern "test/z")))) ;; => Invalid function: test/z

Looking at the arguments of each call, they evaluate to the same thing:

(eq (function test/z) (eval `(function ,(intern "test/z")))) ;; => t

What really has me scratching my head that is that if I use defun instead of defmacro funcall then works in both calls..

I think I'm missing something in my understanding and I'm hoping someone can straighten me out. I'm reading the elisp info manual, but haven't found anything.

What's going on here?

Muihlinn
  • 2,576
  • 1
  • 14
  • 22
theia-jane
  • 80
  • 7
  • 1
    You have probably run into undefined behavior. The [Elisp manual](https://www.gnu.org/software/emacs/manual/html_node/elisp/Calling-Functions.html#Calling-Functions) says "The argument FUNCTION must be either a Lisp function or a primitive function. Special forms and macros are not allowed, because they make sense only when given the unevaluated argument expressions. ‘funcall’ cannot provide these because, as we saw above, it never knows them in the first place." – NickD Jun 14 '20 at 13:02
  • @NickD It **is** undefined behavior if it is explicitly excluded in the manual even if the reasoning does not fit the OP's use-case. – Tobias Jun 14 '20 at 14:02
  • Thank you! I should have read the documentation more carefully.. If one of you will write up an answer, I'll happily accept it to give you credit.. Since funcall does specifically call out that it doesn't work on macros I think that whatever behavior results shouldn't be relied upon. Although, it would be helpful if funcall enforced it's restriction, but that's my opinion. If I want to dynamically call a macro like this I can use: `(eval (list (intern "test/z"))`, which is what I'll do. – theia-jane Jun 14 '20 at 19:00
  • tylerware wrote: "If one of you will write up an answer, I'll happily accept it to give you credit..". @NickD That's your part. Best regards, Tobias. – Tobias Jun 14 '20 at 19:26
  • Done - thanks @Tobias. – NickD Jun 14 '20 at 20:33

1 Answers1

2

`funcall' is not supposed to be called with a macro as the FUNCTION argument. The Elisp manual says:

The argument FUNCTION must be either a Lisp function or a primitive function. Special forms and macros are not allowed, because they make sense only when given the unevaluated argument expressions. ‘funcall’ cannot provide these because, as we saw above, it never knows them in the first place.

However, that does not explain why funcall does not complain on the first call (that may be a bug). Wrapping it in (function ...) seems to make a difference in whether funcall will get an error or not in the macro case. The more common (?) method of passing the symbol to funcall does the right thing according to the doc:

(defmacro test/z () "z")
(funcall (function test/z)) ==> "z"
(funcall 'test/z) ==> invalid function: test/z

(defun func/z () "z")
(funcall (function func/z)) ==> "z"
(funcall 'func/z) ==> "z"
NickD
  • 27,023
  • 3
  • 23
  • 42