Note that there are some explanatory texts on larger screens.

plurals
  1. POLisp: can a macro be recursive?
    primarykey
    data
    text
    <p>I've recently started coding in Lisp, and have already been most impressed with macros - they <a href="https://www.thanassis.space/score4.html#lisp" rel="nofollow noreferrer">allowed me to do complex loop-unrolling</a> at compile-time, something I can't do this elegantly in any other language that I know of (i.e. code-generate while keeping the original structure).</p> <p>On to optimization: I've sprinkled type annotations (lots of "the fixnum") in the same code. Once I've added 3 or 4 of them, I realized I was doing it wrong - this is what macros are for, Dont Repeat Yourself...</p> <pre><code>; whenever we want to indicate that the result of an operation ; fits in a fixnum, we macro expand (the fixnum (...)) (defmacro fast (&amp;rest args) `(the fixnum ,args)) ... (cond (...) (t (let* ((forOrange (+ (aref counts 5) (fast * 2 (aref counts 6)) (fast * 5 (aref counts 7)) (fast * 10 (aref counts 8)))) (forYellow (+ (aref counts 3) (fast * 2 (aref counts 2)) (fast * 5 (aref counts 1)) (fast * 10 (aref counts 0)))) </code></pre> <p>...and indeed, this worked: instead of writing lots of "(the fixnum (...))" everywhere, I just quickly prefix the expression with "fast" - and all is well.</p> <p>But then...</p> <p>I realized that even this is not where things should stop: in principle, the macro "fast" should... be called at the TOP of the evaluation, in this case:</p> <pre><code> (forYellow (fast + (aref counts 3) (* 2 (aref counts 2)) (* 5 (aref counts 1)) (* 10 (aref counts 0)))) </code></pre> <p>...and it should recursively "plant" "(the fixnum (...))" in all subexpressions.</p> <p>Can this be done? Can a "defmacro" be recursive?</p> <p><strong>UPDATE</strong>: I faced some really weird problems trying to do this, so I ended up doing what Rord suggested below - i.e. implemented a function, tested it in the repl, and calling it from the macro:</p> <pre><code>(defun operation-p (x) (or (equal x '+) (equal x '-) (equal x '*) (equal x '/))) (defun clone (sexpr) (cond ((listp sexpr) (if (null sexpr) () (let ((hd (car sexpr)) (tl (cdr sexpr))) (cond ((listp hd) (append (list (clone hd)) (clone tl))) ((operation-p hd) (list 'the 'fixnum (cons hd (clone tl)))) (t (cons hd (clone tl))))))) (t sexpr))) (defmacro fast (&amp;rest sexpr) `(,@(clone sexpr))) </code></pre> <p>And it works fine under SBCL:</p> <pre><code>$ sbcl This is SBCL 1.0.52, an implementation of ANSI Common Lisp. ... * (load "score4.cl") T * (setf a '(+ (1 2) (- 1 (+ 5 6))) ... * (clone a) (THE FIXNUM (+ (1 2) (THE FIXNUM (- 1 (THE FIXNUM (+ 5 6)))))) * (macroexpand '(fast + 1 2 THE FIXNUM (- 1 THE FIXNUM (+ 5 6)))) (THE FIXNUM (+ 1 2 THE FIXNUM (THE FIXNUM (- 1 THE FIXNUM (THE FIXNUM (+ 5 6)))))) T </code></pre> <p>All is well, except for one side-effect: CMUCL works, but no longer compiles the code:</p> <pre><code>; Error: (during macroexpansion) ; Error in KERNEL:%COERCE-TO-FUNCTION: the function CLONE is undefined. </code></pre> <p>Oh well :-)</p> <p><strong>UPDATE</strong>: The compilation failure was addressed and solved in <a href="https://stackoverflow.com/questions/8138714/solved-lisp-macro-calling-a-function-works-in-interpreter-fails-in-compiler">a different SO question</a>.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload