Subject: Re: new to the condition system
From: rpw3@rpw3.org (Rob Warnock)
Date: Sat, 05 May 2007 19:30:49 -0500
Newsgroups: comp.lang.lisp
Message-ID: <0qednV3pdKUkvKDbnZ2dnUVZ_rKvnZ2d@speakeasy.net>
Pascal Costanza  <pc@p-cos.net> wrote:
+---------------
| Joe Marshall wrote:
| > But the advantage of tail calls is that they are more general than
| > a plain GOTO.  You can trivially emulate a GOTO by tail calls that
| > don't pass any arguments, but you have to come up with some
| > sort of ad-hoc convention for passing arguments if you want to
| > emulate tail-calls with GOTO.  ...
| 
| I am not convinced that GO doesn't have any advantages over tail calls. 
| One thing that's good about GO is that you can both have invocation of 
| GO and go tags in arbitrary places within a tagbody. So you can write 
| code like this:
| 
| (tagbody
|    :tag1 ...
|          (when foo (go :tag3))
|          ... ;; fall
|    :tag2 ... ;; through
|          (unless bar (go :tag1))
|          ...
|    :tag3 ...
|          (when baz (go :tag1)))
| 
| The following observations are interesting:
| 
| - Code "covered" by a label can just continue to execute
| into the next  part (see the part marked as "fall through").
+---------------

You can do exactly the same thing with tail calls [at least in
those CLs providing them] by using LABELS instead of TAGBODY, though
you sometimes need to wrap the "GO"s in (RETURN-FROM here (there))
to convert them into true tail calls:

    (labels (
      (tag1 ()
	    ...
	    (when foo (return-from tag1 (tag3)))
	    ...
	    (tag2)) ; "fall through"
      (tag2 ()
	    ...
	    (unless bar (return-from tag2 (tag1)))
	    ...
	    (tag3)) ; "fall through"
      (tag3 ()
	    ...
	    (unless bar (return-from tag2 (tag1)))
	    ...
	    (when baz (tag1)))))

+---------------
| - We can conditional jumps anywhere in the code. These jumps don't
| have to be in tail position, but nevertheless don't grow the stack.(!
| You don't have to rearrange you code such that such jumps are in
| tail position.
+---------------

As demonstrated above, provided your LABELS routine don't do their
"jumps" from inside some binding form that *precludes* tail calls
[such as binding a dynamic variable or a CATCH or HANDLER-{CASE,BIND}
or UNWIND-PROTECT], you can turn any call into a tail-call by simply
wrapping it in (RETURN-FROM here (there)) -- no need to "rearrange"
your code more than that very-local amount.

+---------------
| Of course, one could try to come up with some extension of such a
| GO mechanism where the labels "receive" arguments and GO can pass
| arguments:
+---------------

As demonstrated above, CL already has such: LABELS.


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607