Rainer's answer illustrates the use of tagbody , which is probably the easiest way to implement this type of construct (a special type of goto , or an unconditional jump). I thought it would be nice to note that if you do not want to use the explicit tag or the implicit tag provided by one of the standard constructs, you can also create with-redo in the same way as you suggested. The only difference in this implementation is that we will not specify a tag, since they are not evaluated in the tagbody , and compatibility with other constructs is also good.
(defmacro with-redo (name &body body) `(macrolet ((redo (name) `(go ,name))) (tagbody ,name ,@body))) CL-USER> (let ((x 0)) (with-redo beginning (print (incf x)) (when (< x 3) (redo beginning)))) 1 2 3 ; => NIL
Now this is actually a fuzzy abstraction , because body can define other labels for the implicit tagbody and can use go instead of redo , etc. This may be desirable; many built-in iterative constructs (like do , do* ) use an implicit tagbody , so it might be ok, But since you also add your own control flow operator, redo , you might want to make sure that it can only be used with tags, defined with-redo . In fact, while Perl redo can be used with or without a label, Ruby redo does not seem to allow a shortcut. Unsuccessful cases allow transition behavior to the innermost closed loop (or, in our case, the innermost with-redo ). We can eliminate the fuzzy abstraction, as well as the ability for the redo jack at the same time.
(defmacro with-redo (&body body) `(macrolet ((redo () `(go
Here we have defined a tag for use with with-redo , which other things should not be aware of (and cannot know if they do not macro-expose some forms of with-redo , and we wrapped the body in lambda , which means, for example, the character body is the form to be evaluated, not the tag for the tagbody , here is an example showing that redo returns to the nearest lexically spanning with-redo :
CL-USER> (let ((i 0) (j 0)) (with-redo (with-redo (print (list ij)) (when (< j 2) (incf j) (redo))) (when (< i 2) (incf i) (redo)))) (0 0) (0 1) (0 2) (1 2) (2 2) ; => NIL
Of course, since you can define with-redo yourself, you can decide which design you want to take. You might like the idea of redo not accepting arguments (and disguising go with a secret label, but with-redo is still an implicit tag so you can define other tags and go to them with go ; may also change the code here to do this also.
Some implementation notes
This answer generated some comments, I wanted to make a couple more notes about the implementation. The implementation of with-redo with labels is pretty straight forward, and I think all the answers are posted at; without a shortcut is a little harder.
Firstly, using a local macrolet is a convenience that will lead to warnings with redo , outside of any lexically encompassing with-redo . For example, in SBCL:
CL-USER> (defun redo-without-with-redo () (redo)) ; in: DEFUN REDO-WITHOUT-WITH-REDO ; (REDO) ; ; caught STYLE-WARNING: ; undefined function: REDO
Secondly, using #1=#:hidden-label and #1# means that the go tag for reuse is a non-integer character (which reduces the likelihood of abstraction leakage), but it is also the same character for with-redo decompositions. The following snippet tag1 and tag2 show go tags from two different with-redo extensions.
(let* ((exp1 (macroexpand-1 '(with-redo 1 2 3))) (exp2 (macroexpand-1 '(with-redo abc)))) (destructuring-bind (ml bndgs (tb tag1 &rest rest)) exp1 ; tag1 is the go-tag (destructuring-bind (ml bndgs (tb tag2 &rest rest)) exp2 (eq tag1 tag2)))) ; => T
An alternative with-redo implementation using fresh gensym for each macro extension does not have this guarantee. For example, consider with-redo-gensym :
(defmacro with-redo-gensym (&body body) (let ((tag (gensym "REDO-TAG-"))) `(macrolet ((redo () `(go ,tag))) (tagbody ,tag ((lambda () ,@body)))))) (let* ((exp1 (macroexpand-1 '(with-redo-gensym 1 2 3))) (exp2 (macroexpand-1 '(with-redo-gensym abc)))) (destructuring-bind (ml bndgs (tb tag1 &rest rest)) exp1 (destructuring-bind (ml bndgs (tb tag2 &rest rest)) exp2 (eq tag1 tag2)))) ; => NIL
Now, is it worth asking whether this is of practical importance, and if so, in what cases, and is it the difference for the better or for the worse? Honestly, I'm not quite sure.
If you did complex code manipulation after internal macro definition of the form (with-redo ...) , form 1 so that (redo) has already been turned into (go #1#) , that means moving (go #1#) into the body of another form (with-redo ...) , form 2 , it will still have the effect of restarting the iteration in form 2 . In my opinion, this makes it more similar to return , which can be transferred from block b 1 to another block b 2 , with the only difference being that it now returns from b 2 instead of b 1 . I think this is desirable, because we are trying to consider the unmarked with-redo and redo as primitive control structures.