Search code examples
lambdaemacsmacroselisp-macro

Is defining unnamed macros possible? ("lambda" macros?)


My goal is to evaluate an expression at compile time, like some simple things as (+ 1 1). But, I would like an integer "2" to be compiled in, instead of a full "1+1" operation to be done at runtime, without using a named macro. This makes my source code clearer showing how I got the value "2" while not wasting CPU time repeating the same "1+1" operation. Here is a simple example illustrating the basic idea but not for real cases, say, my ideal goal defun function looks like this:

(defun test ()
   (+ 1 2 3))

I would like literal "2" to be evaluated at compile time so I use eval-when-compile:

(defun test ()
   (+ 1 
      (eval-when-compile (+ 1 1)) 
      3))

However, it turns out becoming:

(defun test ()
   (+ 1 '2 3))

It's of course okay for this simple case but this extra quote is causing problems for more complicated cases.

Defining a named macro works:

(defmacro 1+1 () `,(+ 1 1))
(defun test ()
   (+ 1 (1+1) 3))

It will produce my ideal result with no quote prepending the integer "2":

(defun test ()
    (+ 1 2 3))

Is there a simple way like the above eval-when-compile to achieve this, in a defun? Something like an unnamed macro to get rid of the named helper 1+1, or should I call this "lambda macro"?


Solution

  • Inspired by the answer of @phils, I finally able to define anonymous macros and run them. I define a helper macro named immediate to help solving this; it basically defines an anonymous macro function temporarily using macrolet, then runs it:

    (defmacro immediate (body)
      "Define an anonymous macro and run it immediately"
      (let ((f (make-symbol "immed")))
        `(macrolet ((,f () ,body))
           (,f))))
    

    With immediate, I can rewrite the sample code in my original question this way:

    (defun test ()
      (immediate (concat "Docstring" " of " "test" ))
      (+ 1
         (immediate (+ 1 1))
         3))
    

    The test function will be defined exactly as what I expected it to be:

    (defun test ()
      "Docstring of test"
      (+ 1 2 3))
    

    (The name immediate is inspired by the immediate words from the Forth language.)