Since my first posts on my own little control macros (while, foreach etc.) I’ve learned quite a bit and so here are my revised versions of some of them, since not all of them would work outside of an interpreter, since they were recursive macros.
A better, and also non recursive way to write macros is to either use exisiting macros (as done with my for macro) or for example by using the tagbody & go constructs of lisp (similar to a goto in C):
new while macro
1 2 3 4 5 6 7 8 | ;; while macro with tagbody & go. (defmacro while-new (condition &body body) (let ((tag-name (gensym))) `(tagbody ,tag-name ,@body (when ,condition (go ,tag-name))))) |
Thanks again to the anonymous commenter ‘foo’, who made it clear, that the previous version (without gensymed-symbols) could make trouble in the case of a caller having symbols defined with the same name (in this case tag-name).
I also changed the foreach-macro to use gensymed symbols:
new foreach macro
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ;; foreach macro with tagbody & go. (defmacro foreach-new ((var list) &body body) (let ((tmp-list (gensym)) (start-tag (gensym))) `(let* ((,tmp-list ,list) (,var nil) (rest (cdr ,tmp-list))) (tagbody ,start-tag (if (null ,tmp-list) nil (progn (setf ,var (car ,tmp-list)) (setf rest (cdr ,tmp-list)) (if ,var (progn ,@body (setf ,tmp-list rest) (go ,start-tag)) nil))))))) |
new until macro
1 2 3 4 | ;; until macro reusing new while macro. (defmacro until-new (cond &body body) `(while-new (not ,cond) ,@body)) |
Also, thanks to ‘foo’, who pointed this out in some comments.
I’ve known about this fact for little while already, but haven’t thought about posting it here. Since some people might be reading this and wondering, why they don’t seem to work, they now should have some working samples.


One Response
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.
TOP, TMP and START interfere with user code. The user might also want to use those symbols.
Destructuring like that won’t work. Think of a list: (nil nil nil).
CAR of that list is nil. The usual way to write recursion is (if (null list) (end-clause) (let ((element (first list)) (rest (rest list))) …).
Also think if you really want to rebind the foreach variable in each iteration?