I’m trying to understand if it’s possible/good practice to write macros that expand differently depending on whether the arguments are constants or symbols. As an example (in Emacs 30.0.50):
(defmacro list-length (x)
(cond (`(symbolp ,x) `(eval (length ,x)))
(t (length x))))
The different ways I’d expect it to be expanded are as follows:
1.
(let ((x (list 1 2 3 4 5)))
(macroexpand '(list-length x)))
=> (eval (length x))
OR
2.
(macroexpand '(list-length (1 2 3 4)))
=> 4
A more real-world use case for myself is in something like https://github.com/nealsid/simpleproj/blob/main/util.el#L18, and what I’d like to do in particular is either expand to several calls to puthash if the macro arguments can be determined at expansion time, or otherwise expand to a cl-loop which processes elements of the list passed in at run-time. What exactly “determined at expansion time” means is a bit fuzzy to me (symbolp? Boundp? Or if there is something more I am missing). Thanks!
The code you quoted follows this structure:
Let's assume first that you rename the function
fun%instead, and define a macro namedfun, which at first can be as simple as that:Each time you invoke
fundirectly, you will know how many parameters you have:Syntactically, this is a call with 4 arguments, the macro can access it through
argsvariable which is bound to(:a 0 :b 1). So there is no case where the macro doesn't know how many arguments are passed to the call. Only the functionfun%can receive an arbitrary number of arguments when you call it usingapply:But macros cannot be applied like that (you generally don't invoke the macro-function yourself, the macroexpansion mechanism do it for you).
The situation would be different if
argswas not a&restargument, if you had instead:Here you could only invoke
funwith exactly one argument:You have two common situations, both of which allow you to either optimize during expansion or defer to
foo%:You want
funto have the same evaluation mechanism as usual functions, ie. you want the<list>expression to be evaluated as-is at runtime, and then use the value to populate the list. If you do so, then you can optimize for some known cases, like the list being constant (it is a quoted list). You will have to defer to a call tofoo%anytime you don't know ifargsis a constant list or not.You are defining a special form where standard evaluation rules no longer apply. For example, you decide that the set of keys is probably always the same, and only the values need to be evaluated. You specify your own grammar:
KEYWORD items are expected to be literal symbols, but FORM values must be injected in the generated code at the place where their value is needed (in the calls to
puthash).Valid calls are:
Note how the list is not quoted, it is part of the syntax of
foo, it can be parsed at runtime and you can know its size, the only unknonw things in the macro are the values associated with the fixed set of keys.Both are possible, you generally never need to call
eval, because how could the compiler ever know what the values are going to be when the code is run, in a totally different environment? Consider this Common Lisp code:If
funis a macro, you cannot know how many items thelisthas, this is something that will be known after the user answer the prompt.