From: Spiros Bousbouras
Subject: Questions about 5.2 of the HS
Date: 
Message-ID: <e8a0363f-d5f0-4149-8281-ec9e88594128@m44g2000hsc.googlegroups.com>
In section 5.2 of the HS we read:

    1. Intervening exit points are ``abandoned''
    (i.e., their extent ends and it is no longer
    valid to attempt to transfer control through
    them).

Intervening between what and what ? Intervening in space
or in time ?

Again from 5.2:

    Note that for go, the exit point is the form within
    the tagbody that is being executed at the time the go
    is performed

If I understand things correctly then this is not completely
accurate. An exit point counts as an exit point regardless of
whether any form is being executed , right ? In the following
example (which is not meant to clarify abandoment of exit
points but simply what counts as an exit point)...

(tagbody
    (go b)
  a (go c)
  b (print 12)
    (go d)
  c (print 13)
  d)

...the (go c) form counts as an exit point although the form
won't be executed , yes ?

From: Kaz Kylheku
Subject: Re: Questions about 5.2 of the HS
Date: 
Message-ID: <0ce4457f-8f66-45d3-88d4-54bd66f18d97@v23g2000pro.googlegroups.com>
On Apr 28, 4:55 am, Spiros Bousbouras <······@gmail.com> wrote:
> In section 5.2 of the HS we read:
>
>     1. Intervening exit points are ``abandoned''
>     (i.e., their extent ends and it is no longer
>     valid to attempt to transfer control through
>     them).
>
> Intervening between what and what ? Intervening in space
> or in time ?

My interpretation is that this refers to nested dynamic contours. If A
dynamically encloses B, and B dynamically encloses C, then if we
abandon C to jump back to A, we have to ``cross'' the intervening B.
Its dynamic bindings (special variables, restarts, hahdlers,
catches, ...) have to be undone.

The term ``contour'' as used by Kent Pitman and others is very nice
because you can visualize it as a line of equal elevation, pressure,
etc, on a map.

If we descend from a 1200 foot mountain, from the 1100 foot elevation
to the 900 foot elevation you will necessarily cross an intervening
1000 foot elevation line, no matter what path we take.

> Again from 5.2:
>
>     Note that for go, the exit point is the form within
>     the tagbody that is being executed at the time the go
>     is performed
>
> If I understand things correctly then this is not completely
> accurate. An exit point counts as an exit point regardless of
> whether any form is being executed , right ?

A form is always being executed. The simplest form of GO within a
TAGBODY is one which is not wrapped in any form, e.g. (TAGBODY (GO
FOO) FOO). This is still a form. So here, the exit point is the (GO
FOO) form.  I.e. the exit point is the ``tagbody-level'' form from
which GO is branching out. If you imagine the TAGBODY to be a kind of
``local top-level'', then a GO always terminates this local toplevel
form which encloses it. There cannot be a GO within a form:

  (TAGBODY (PROGN (GO FOO) FOO)) ;; invalid

  (TAGBODY (PROGN (GO FOO)) FOO) ;; valid

Here, in the correct second example, the exit point is the entire
(PROGN ...) form containing the GO. When the GO is evaluated, that
entire form is bailed all the way back to the TAGBODY level.

 In the following
> example (which is not meant to clarify abandoment of exit
> points but simply what counts as an exit point)...
>
> (tagbody
>     (go b)
>   a (go c)
>   b (print 12)
>     (go d)
>   c (print 13)
>   d)
>
> ...the (go c) form counts as an exit point although the form
> won't be executed , yes ?

The (GO C) form would be an exit point if it were executed. If it's
not being executed, then the classification is not relevant.

If you like, you can consider all forms in a TAGBODY which somewhere
contain a GO (a GO which matches that TAGBODY and not some inner,
nested one) to be exit points, regardless of whether or not they are
reachable code.

Similarly, an unused variable is still a binding, and a function that
is never called is still a function, right?  Things that disappear
under optimization are not recategorized as something different back
in the abstract semantics.
The wording of 5.2 is such that the exit point is defined in a
situational way: ``the exit point is the form within the tagbody that
is being executed at the time the go is performed''. But this
relationship is lexical, or static, if you will. The GO form is
lexically embedded in its surrounding expression, which is lexically
embedded in the TAGBODY. So there cannot be any run-time variation in
identifying the exit form.

However, although the exit form is statically determined, the set of
intervening exit forms may be dynamic! The reason is that the GO may
be wrapped inside the body of a closure which is reached indirectly.

Example:

  (defun unwinder (fun) (unwind-protect (funcall fun) (print
"unwind!"))

  (tagbody (unwinder #'(lambda () (go foo)) foo)

Here, we see the output "unwind!" as a result of the GO to label FOO.
The UNWIND-PROTECT is considered an intervening exit form. Of course,
UNWINDER can be a completely arbitrary function which establishes
arbitrary dynamic contours around the call to the closure.

And there is a dynamic component in the relationship betwen GO and
TAGBODY. (Just like there is a dynamic component in the relationship
between lexical variables and the form in which they are defined:
namely the instantiation). The tags have lexical scope, but dynamic
extent. It's possible, via closures, to reach a GO call which refers
to a label in a TAGBODY which has already terminated. Such a GO cannot
be realized; although the GO tag is lexically visible, its extent has
already ended.
From: Spiros Bousbouras
Subject: Re: Questions about 5.2 of the HS
Date: 
Message-ID: <08f54d3f-9a69-446b-8241-566d84505c64@24g2000hsh.googlegroups.com>
On 28 Apr, 20:09, Kaz Kylheku <········@gmail.com> wrote:
> The simplest form of GO within a
> TAGBODY is one which is not wrapped in any form, e.g. (TAGBODY (GO
> FOO) FOO). This is still a form. So here, the exit point is the (GO
> FOO) form.  I.e. the exit point is the ``tagbody-level'' form from
> which GO is branching out. If you imagine the TAGBODY to be a kind
> of
> ``local top-level'', then a GO always terminates this local toplevel
>
> form which encloses it. There cannot be a GO within a form:
>
> (TAGBODY (PROGN (GO FOO) FOO)) ;; invalid
>
> (TAGBODY (PROGN (GO FOO)) FOO) ;; valid
>
> Here, in the correct second example, the exit point is the entire
> (PROGN ...) form containing the GO. When the GO is evaluated, that
> entire form is bailed all the way back to the TAGBODY level.

Two questions about this:

1) In the second example how is it decided that the exit point is
the whole (PROGN (GO FOO)) and not just (GO FOO) ? I presume if
we change the example to be (TAGBODY (PROGN (PROGN (GO FOO))) FOO)
then the exit point corresponding to (GO FOO) will be the whole
(PROGN (PROGN (GO FOO))) , yes ?

2) In the first paragraph of 5.2 it says:

    Note that for go, the exit point is the
    form within the tagbody that is being executed
    at the time the go is performed; for return-from,
    the exit point is the corresponding block form;
    and for throw, the exit point is the corresponding
    catch form.

It seems to me there is some asymmetry between on one side what
it says about go and on the other what it says about return-from
and catch. The symmetrical thing to say would be that the exit
point for a go is the corresponding tagbody. I don't understand
what is the significance of this asymmetry.


In fact I was wondering whether 5.2 actually contains a mistake
and it meant to say that the exit point for a go is the
corresponding tagbody. Take for example the following code:

(tagbody
  a (go c)
  b (go d) ; Point B
  c (print "hello")
  d
; ... More code ...
) ; Closes the tagbody

In this code it is legal inside "More code" to jump
to the tag b but if we take 5.2 literally it suggests
that it isn't legal: the form (go d) is an exit point
and is intervening between (go c) and the target of
(go c) so it should be abandoned once (go c) gets
executed. On the other hand if 5.2 said that the exit
point for (go d) is the tagbody then it's not intervening
so all is ok.

> The wording of 5.2 is such that the exit point is defined in
> a situational way: ``the exit point is the form within the
> tagbody that is being executed at the time the go is
> performed''. But this relationship is lexical, or static, if
> you will. The GO form is lexically embedded in its
> surrounding expression, which is lexically embedded in the
> TAGBODY. So there cannot be any run-time variation in
> identifying the exit form.

And this is exactly why I said that the choice of words in 5.2
is inaccurate. When it says "being executed" it makes it
sound as if exit points depend on what will happen at
runtime , not that the "relationship is lexical, or static".

> (defun unwinder (fun)
>   (unwind-protect (funcall fun) (print "unwind!")))
>
> (tagbody (unwinder #'(lambda () (go foo))) foo)
>
> Here, we see the output "unwind!" as a result of the GO to label
> FOO. The UNWIND-PROTECT is considered an intervening exit form.
(I have added 2 closing parentheses in the quoted code above.)

I don't see how UNWIND-PROTECT can be intervening. The way I
see it "intervening" applies when you have at least 2 exit
points and activating one of them causes the other to be
abandoned because it was intervening. But in your code the
only exit point is the one corresponding to (go foo).

To test my understanding , in your code "the form within
the tagbody that is being executed at the time the go is
performed" is (unwinder #'(lambda () (go foo))) , so this
is the exit point corresponding to (go foo) , yes ?
From: Kaz Kylheku
Subject: Re: Questions about 5.2 of the HS
Date: 
Message-ID: <df33699b-734f-43f0-b0c3-8040769c23dd@c19g2000prf.googlegroups.com>
On Apr 30, 9:07 am, Spiros Bousbouras <······@gmail.com> wrote:
> On 28 Apr, 20:09, Kaz Kylheku <········@gmail.com> wrote:
>
> > form which encloses it. There cannot be a GO within a form:
>
> > (TAGBODY (PROGN (GO FOO) FOO)) ;; invalid
>
> > (TAGBODY (PROGN (GO FOO)) FOO) ;; valid
>
> > Here, in the correct second example, the exit point is the entire
> > (PROGN ...) form containing the GO. When the GO is evaluated, that
> > entire form is bailed all the way back to the TAGBODY level.
>
> Two questions about this:
>
> 1) In the second example how is it decided that the exit point is
> the whole (PROGN (GO FOO)) and not just (GO FOO) ? I presume if
> we change the example to be (TAGBODY (PROGN (PROGN (GO FOO))) FOO)
> then the exit point corresponding to (GO FOO) will be the whole
> (PROGN (PROGN (GO FOO))) , yes ?
>
> 2) In the first paragraph of 5.2 it says:
>
>     Note that for go, the exit point is the
>     form within the tagbody that is being executed
>     at the time the go is performed; for return-from,
>     the exit point is the corresponding block form;
>     and for throw, the exit point is the corresponding
>     catch form.

Note that the form being executed isn't necessarily the one which
contains the GO!

  (let (fun)
    (tagbody
      (setf fun (lambda () (go exit)))
      (funcall fun)
     exit))

At the time of the GO, which form is being executed? The (FUNCALL FUN)
of course.  So this is the form which is terminated by the GO. Once it
is terminated, then control can take place to the EXIT label.

Now if we look at the Glossary entry for ``exit point'', it says that
an exit point is actually a place within a control form where control
ends up, or through which control passes or returns. It also says that
``tagbody establishes a binding for an exit point with lexical extent
to which go can transfer control''.

Lexical extent??? What? As far as I know, there is no such thing. If
we Google for this term, we find that this is the noly place it
appears in the CLHS. It seems to crop up in informal writing and
discussions, with no consistent meaning from writer to writer. And
take a look at 3.1.6 which asserts that ``[t]he argument fails only
because exit points have dynamic extent''.

I think that what this is intended to say is that tagbody establishes
a binding with dynamic extent. I.e. tagbody sets up some kind of
hidden exit binding (not to be confused with a label!) around a form.
A control transfer initiated by GO first goes to this point, and then
from there to the target label.

This point has to be found dynamically, or else in the above example,
lexical scope would route (GO EXIT) to an exit point associated with
the (SETF ...), which is absurd.

My interpretation of 5.2 with respect to GO is that it is the final
step 4 which achieves the transfer of control to the given label,
which can be identified as the target.

So in other words, steps 1 to 3 have only to do with terminating the
form which  dynamically (not necessarily lexically!) surrounds the
GO.  The possibility of exit forms intervening has to do with forms
which are nested, not those which are spanned by the final
unconditional branch to label, which no longer has anything to do with
any kind of processing of exit forms or unwinding.

Clearly, though, various instances of wording leave something to be
desired.

I will close off this posting with an example of what I think is an
intervening endpoint that is abandoned in step, or should I say, event
1:

(tagbody
  (block abandoned
    (tagbody
      (go exit)))
 exit)

The exit point for the GO is a dynamically bound point within the
outer-most TAGBODY, tied to the form (BLOCK ABANDONED ...).

The inner TAGBODY also establishes such an exit point, around (GO
EXIT). That exit point is abandoned by the (GO EXIT) control transfer.

The block also establishes an exit point for any RETURN-FROM forms.
That exit point is also intervening to the (GO EXIT) and is also
abandoned.

Once these are abandoned, we move to events 2 and 3, which are
interleaved: UNWIND-PROTECT forms are processed and dynamic bindings
undone. We have none, so we move to event 4: terminate the extent of
the exit point (leave the BLOCK form) and transfer control to the
target: i.e. jump to the EXIT label.

One interesting observation is that the intervening exit points are
terminated first, in event 1. This is not interleaved with the
processing of UNWIND-PROTECT, and undoing of bindings of catch tags
and dynamic variables, in events 2 and 3. This means that the cleanup
clauses of UNWIND-PROTECTS must not have any of these intervening exit
points visible to them; they are already gone, all the way up to (but
not including) the targeted exit form!

Example:

  (tagbody
    (block try-return
      (unwind-protect
        (go exit)
        (return-from try-return)))
   exit)

This should complain that no block called TRY-RETURN is visible. If
the whole thing is encased in another block called TRY-RETURN, it
should go to that one and not the inner one.

In CLISP, it appears that the block's intervening exit point is still
visible when the UNWIND-PROTECT is being processed. The exit is
reached and the TAGBODY terminates, returning NIL with no errors
signaled. In other words, its binding is interleaved with other
dynamic bindings.

It works the same way in Franz's Allegro Common Lisp.

I think that these implementations are doing what is sane; it is the
four-event model of control transfer in section 5.2 that is
defective.
From: Kaz Kylheku
Subject: Re: Questions about 5.2 of the HS
Date: 
Message-ID: <e32b9025-fe79-4008-a7ea-1406e92cdeef@w5g2000prd.googlegroups.com>
On May 1, 10:26 pm, Kaz Kylheku <········@gmail.com> wrote:
> In CLISP, it appears that the block's intervening exit point is still
> visible when the UNWIND-PROTECT is being processed. The exit is
> reached and the TAGBODY terminates, returning NIL with no errors
> signaled. In other words, its binding is interleaved with other
> dynamic bindings.
>
> It works the same way in Franz's Allegro Common Lisp.
>
> I think that these implementations are doing what is sane; it is the
> four-event model of control transfer in section 5.2 that is
> defective.

Haha, it's not so simple. There is a lengthy cleanup issue attached to
this, oh boy. The cltl wasn't clear about this, and some historic
implementations ( do the termination of intervening exit points first,
just like 5.2 requires. Quote:

``Both implementations of Symbolics Genera (3600 and Ivory) end the
extent
of a target BLOCK or CATCH at the moment the values are returned, and
end the extent of a passed-over exit at the moment the THROW, RETURN,
or
GO commences.  This choice of extent maximizes efficiency within the
particular stack structure used by these implementations, by avoiding
the need to retain the control information needed to use a passed over
exit through the transfer of control.  Genera signals an error if an
attempt is made to use an exit that has been passed over.''

Eek!

Spiros, I think you might also benefit from reading the whole cleanup
issue, because it explains a lot of things very well. Even just having
the main ideas collected in one place and restated in different
wording is helpful.