; SBCL
(let ((a 123) (b 1))
(tagbody
tag
(format t "Here~%")
(when (= b 1)
(incf b)
(setq a (go tag))))
a)
; in: LAMBDA NIL
; (SETQ A (GO TAG))
;
; note: deleting unreachable code
;
; compilation unit finished
; printed 1 note
Here
Here
123
--
Who's your mama ?
Does a one-handed clap make a sound?
Why would something that doesn't return have a return value?
> The HS gives
> go tag =>|
> but I couldn't find anything in the notation section for |.
This is a problem.
> I assume there's no return value and the following experiment seems to
> confirm it:
>
> ; SBCL
> (let ((a 123) (b 1))
> (tagbody
> tag
> (format t "Here~%")
> (when (= b 1)
> (incf b)
> (setq a (go tag))))
> a)
The evaluation of this SETQ form does not complete! It is interrupted by a
control transfer. This happens while evaluating the value to be stored, which
is specified by the form (go tag). So no value is stored into a, since the
setq form is abandoned while preparing the new value to be stored.
The evaluation of the (when ...) is also abandoned. The exit point for the
control transfer is the tagbody (which isn't abandoned: it proceeds by passing
control to the specified tag).
If you hit it against your thigh it most definitely does.
> Why would something that doesn't return have a return value?
Since the vast majority of Lisp forms return a value for reasons of
uniformity perhaps ? To make the language more functional ? Which makes
me wonder whether pure functional languages gave gotos and if they do
how they handle similar constructs.
Since Lisp is an imperative language, not a functional one, there's no
need for this uniformity. Operators like GO, RETURN, and THROW can
never return because they cause execution to resume somewhere else, so
they don't have a return value. They use the same syntax as function
calls, but they're not true functions.
I wouldn't expect purely functional languages to have constructs like
this, but I'm not very familiar with them. I'm sure one of the Haskell
folks who like to troll here can answer definitively.
--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
Kaz is wrong, GO returns 'NEVER.
The proof:
C/USER[30]> (tagbody
(assert (eq 'never (go done)))
done)
NIL
--
__Pascal Bourguignon__
> Kaz is wrong, GO returns 'NEVER.
>
> The proof:
>
> C/USER[30]> (tagbody
> (assert (eq 'never (go done)))
> done)
> NIL
more precisely:
CL-USER 6 > (tagbody
(assert (eq 'pascal-b-is-yanking-your-chain (go done)))
done)
NIL
--
Raffael Cavallaro
It's true that the most Lisp forms return a value, and you are looking
at an exception. Although functions may return zero values (via
VALUES), we're looking at something that isn't a function; GO is a
special operator. Some special operators, of course, produce values,
but some don't. The syntax for "|" (I had to dig for it, but I was
curious too) is documented in �1.4.4.20.4.2 Unconditional Transfer of
Control in the ``Syntax Section''[1] which states (notice that it
specifically uses the term "operators"):
""Some operators perform an unconditional transfer of control, and so
never have any return values. Such operators are notated using a
notation such as the following:
F a b c =>|""
As Kaz mentioned, there's no way for anything to consume the result of
GO, even if it did produce a result. A simpler example using assignment
shows that in (setf *x* (go end)), the value of *x* doesn't get changes
at all:
CL-USER> (defparameter *x* 'x)
*X*
CL-USER > (tagbody
(setf *x* (go end))
end)
NIL
CL-USER > *x*
X
Although CL isn't a pure functional language, there are similarities and
CL can often be used purely and functionally. It would be interesting
to see how other pure and not-so-pure functional-ish languages handle
these sorts of situations. Another good thing to compare is condition
handling. And sure enough, of five restart functions in CL (ABORT,
CONTINUE, MUFFLE-WARNING, STORE-VALUE and USE-VALUE), two (ABORT and
CONTINUE) use the "=>|" notation [2].
//JT
[1] http://www.lispworks.com/documentation/HyperSpec/Body/01_ddtdb.htm
[2] http://www.lispworks.com/documentation/HyperSpec/Body/f_abortc.htm
But GO doesn't return at all. How could it possibly return a value, if
it never returns?
> To make the language more functional ?
Could you sketch your idea of any code at all, that could even notice?
Let's say in one implementation, GO's "return value" is defined to be T.
And in another, GO's "return value" is defined to be NIL.
Please explain how you would ever be able to tell which was which.
-- Don
_______________________________________________________________________________
Don Geddis http://don.geddis.org/ d...@geddis.org
Everybody wants to go to heaven, but nobody wants to die.
You mean more dysfunctional. Go having a return value is like the sound
of one hand clapping.
> Could you sketch your idea of any code at all, that could even notice?
> Let's say in one implementation, GO's "return value" is defined to be T.
> And in another, GO's "return value" is defined to be NIL.
>
> Please explain how you would ever be able to tell which was which.
t = goto
nil = comefrom
simple really.
(let ((a 0))
(tagbody
tag
....
(when (= a 0)
(setq a (go tag)))
...))
Lisp could have worked in such a way that a gets modified. If (go tag)
returned something other than 0 (it might return the name of the tag it
jumped to) then you could use the technique to make sure that you jump
backwards at most once. I'm not saying it would be an improvement if
things worked this way but they might and it wouldn't derail the
semantics of go any more than unwind-protect does.
But when I first asked the question I was thinking more around the
lines that if you want to use formal methods to prove things about a
programme it would probably make things easier if every form returned a
value. But I suspect that Lisp is too messy to allow for wide use of
formal methods anyway ; this is more suited to pure functional
languages.
> On Mon, 30 Nov 2009 10:24:55 -0800
> Don Geddis <d...@geddis.org> wrote:
>> Spiros Bousbouras <spi...@gmail.com> wrote on Mon, 30 Nov 2009:
>> > On Mon, 30 Nov 2009 03:31:14 +0000 (UTC) Kaz Kylheku <kkyl...@gmail.com> wrote:
>> >> Why would something that doesn't return have a return value?
>> >
>> > Since the vast majority of Lisp forms return a value for reasons of
>> > uniformity perhaps?
>>
>> But GO doesn't return at all. How could it possibly return a value, if
>> it never returns?
>>
>> > To make the language more functional ?
>>
>> Could you sketch your idea of any code at all, that could even notice?
>> Let's say in one implementation, GO's "return value" is defined to be T.
>> And in another, GO's "return value" is defined to be NIL.
>>
>> Please explain how you would ever be able to tell which was which.
>
> (let ((a 0))
> (tagbody
> tag
> ....
> (when (= a 0)
> (setq a (go tag)))
> ...))
This is not good.
Try:
(let ((a 'unset))
(tagbody
(setf a (go next))
next
(case a
((t) (print '(go set a to t)))
((nil) (print '(go set a to nil)))
((unset) (print '(go did not set a)))
(otherwise (print `(go set a to ,a which is unexpected))))))
> Lisp could have worked in such a way that a gets modified. If (go tag)
> returned something other than 0
Then it would have broken because = takes only numbers.
> (it might return the name of the tag it
> jumped to) then you could use the technique to make sure that you jump
> backwards at most once. I'm not saying it would be an improvement if
> things worked this way but they might and it wouldn't derail the
> semantics of go any more than unwind-protect does.
>
> But when I first asked the question I was thinking more around the
> lines that if you want to use formal methods to prove things about a
> programme it would probably make things easier if every form returned a
> value. But I suspect that Lisp is too messy to allow for wide use of
> formal methods anyway ; this is more suited to pure functional
> languages.
I guess Disney has some forward influence on people minds. We're
already on the other side of the mirror...
--
__Pascal Bourguignon__
> Lisp could have worked in such a way that a gets modified. If (go tag)
> returned something other than 0 (it might return the name of the tag it
> jumped to) then you could use the technique to make sure that you jump
> backwards at most once.
But how could it do that? I mean, think of the code emitted by such a
construct, for instance: GO is a branch instruction (it is more than
that, of course): after you've executed the branch, whatever was going
to happen in the straight-line code that followed it is not going to
happen any more.
> > Could you sketch your idea of any code at all, that could even notice?
> > Let's say in one implementation, GO's "return value" is defined to be T.
> > And in another, GO's "return value" is defined to be NIL.
> >
> > Please explain how you would ever be able to tell which was which.
>
> (let ((a 0))
> (tagbody
> tag
> ....
> (when (= a 0)
> (setq a (go tag)))
> ...))
>
> Lisp could have worked in such a way that a gets modified. If (go tag)
> returned something other than 0 (it might return the name of the tag it
> jumped to) then you could use the technique to make sure that you jump
> backwards at most once. I'm not saying it would be an improvement if
> things worked this way but they might and it wouldn't derail the
> semantics of go any more than unwind-protect does.
Um, I still don't see it. The fundamental problem is that you have to
evaluate the argument "(go tag)" to SETQ before you have a value to use
to set the variable. But the act of evaulation GO is to transfer
control to the TAG. So, before you have a value you can use, you have
already moved the execution of the program to another point.
You can't realistically have any other result for GO. I mean, what are
the semantics supposed to be? The GO form returns a value, then has to
wait for some other function to finish execution, and THEN transfer
control? How would you know exactly where in this process of argument
evaluation and function application to stop an insert the jump?
What about code like:
(let ((v nil)
(result 0))
(tagbody
tryagain
(setq v (get-value-from-user "Enter number: "))
(setq result (/ 128 (if (zerop v) (go tryagain) v))))
result)
At what point does the upward percolation of values stop and the
transfer of control begin?
> But when I first asked the question I was thinking more around the
> lines that if you want to use formal methods to prove things about a
> programme it would probably make things easier if every form returned a
> value.
Well, if it makes you feel better, you can pretend that GO returns any
value you like. You still have to deal with the problem of the transfer
of control. Now, I haven't looked at formal program proof methods in
decades, but it seems to me that having any sort of control transfer of
this nature will greatly complicate any proofs you might construct.
> But I suspect that Lisp is too messy to allow for wide use of
> formal methods anyway ; this is more suited to pure functional
> languages.
I would think so. You would want to apply formal methods to either a
subset of lisp, or to a domain-specific programming language implemented
in Lisp that has more proof-friendly semantics and restrictions.
--
Thomas A. Russ, USC/Information Sciences Institute
I don't see what point you're trying to make. For one thing what are
your assumptions regarding the return value of (go next) ?
> > Lisp could have worked in such a way that a gets modified. If (go tag)
> > returned something other than 0
>
> Then it would have broken because = takes only numbers.
Ok , so use eql instead of =.
> > (it might return the name of the tag it
> > jumped to) then you could use the technique to make sure that you jump
> > backwards at most once.
[...]
> I guess Disney has some forward influence on people minds. We're
> already on the other side of the mirror...
Huh ?
Well, we could design an extension of the come-from statement.
(let ((i 0))
(tagbody+
(go-to next 42)
loop
(print (take-from))
(go-to next 0)
next
(when (= 42 (take-from))
(go-to loop 12))))
So the result returned by this GO-TO, the second argument, would be
obtained by the TAKE-FROM function.
But indeed, we should do without the GO operator, it is well known
that COME-FROM leads to clearner programs, and more easily provable,
since the post-assertions are more definite.
--
__Pascal Bourguignon__
In a similar manner to how unwind-protect does it perhaps ?
> I mean, think of the code emitted by such a
> construct, for instance: GO is a branch instruction (it is more than
> that, of course): after you've executed the branch, whatever was going
> to happen in the straight-line code that followed it is not going to
> happen any more.
So you arrange the "straight-line code" to appear before the branch.
What's the problem ?
--
Should array indices start at 0 or 1 ? My compromise of 0.5 was
rejected without, I thought, proper consideration.
Stan Kelly-Bootle
None. That's the point. We don't know what it returns, so we test
the result.
>> > (it might return the name of the tag it
>> > jumped to) then you could use the technique to make sure that you jump
>> > backwards at most once.
>
> [...]
>
>> I guess Disney has some forward influence on people minds. We're
>> already on the other side of the mirror...
>
> Huh ?
http://www.imdb.com/title/tt1014759
--
__Pascal Bourguignon__
Does this present any bigger problems than unwind-protect ?
> You can't realistically have any other result for GO. I mean, what are
> the semantics supposed to be? The GO form returns a value, then has to
> wait for some other function to finish execution, and THEN transfer
> control? How would you know exactly where in this process of argument
> evaluation and function application to stop an insert the jump?
>
> What about code like:
>
> (let ((v nil)
> (result 0))
> (tagbody
> tryagain
> (setq v (get-value-from-user "Enter number: "))
> (setq result (/ 128 (if (zerop v) (go tryagain) v))))
> result)
>
> At what point does the upward percolation of values stop and the
> transfer of control begin?
Good question and I don't know the answer. Your example shows that it
would be messy to define precise semantics and if you did it would
complicate the use of go so things are probably better as they are.
> > But when I first asked the question I was thinking more around the
> > lines that if you want to use formal methods to prove things about a
> > programme it would probably make things easier if every form returned a
> > value.
>
> Well, if it makes you feel better, you can pretend that GO returns any
> value you like. You still have to deal with the problem of the transfer
> of control. Now, I haven't looked at formal program proof methods in
> decades, but it seems to me that having any sort of control transfer of
> this nature will greatly complicate any proofs you might construct.
So you're saying that the transfer of control would present greater
problems for formal proofs than whether go returns a value or not ,
yes ?
--
The right of people to keep and arm bears shall not be infringed.
> So you arrange the "straight-line code" to appear before the branch.
> What's the problem ?
Unfortunately, reordering the order in which events happen tends to
have fairly bad effects on a lot of programs, especially those which
use things like branches to ensure that some things don't happen at
all. Indeed, many (all?) modern processors, which often *do* do things
which should never happen when they speculate both sides of a branch,
have go to enormous lengths to make it appear that the untaken branch
never happened.
> On Mon, 30 Nov 2009 22:29:08 +0000
> Tim Bradshaw <t...@cley.com> wrote:
> > On 2009-11-30 21:32:15 +0000, Spiros Bousbouras <spi...@gmail.com> said:
> >
> > > Lisp could have worked in such a way that a gets modified. If (go tag)
> > > returned something other than 0 (it might return the name of the tag it
> > > jumped to) then you could use the technique to make sure that you jump
> > > backwards at most once.
> >
> > But how could it do that?
>
> In a similar manner to how unwind-protect does it perhaps ?
How would you decide how far back to "unwind"? For example, what would
the following things do?
(f (g (h ... (omega (go tag)) ...)))
or
(setq x (setq y (setq z .... (setq omega (go tag))))...)))
or
(list (setq x 1) (go tag) (setq x 2))
or
(progn (setq x 1) (go tag) (setq x 2))
or
((lambda (x) (setq y x)) (go tag))
?
rg
No idea. See my reply to Thomas Russ.
But also:
(tagbody
(assert (not (eq 'never (go done))))
done)
Go allows logical contradictions:
(tagbody
(assert (let ((go-result (go done)))
(not (eql go-result go-result))))
done)
> On 30 Nov 2009 14:37:41 -0800
> t...@sevak.isi.edu (Thomas A. Russ) wrote:
> > Spiros Bousbouras <spi...@gmail.com> writes:
> >
> > >
> > > (let ((a 0))
> > > (tagbody
> > > tag
> > > ....
> > > (when (= a 0)
> > > (setq a (go tag)))
> > > ...))
> > >
> > > Lisp could have worked in such a way that a gets modified. If (go tag)
> > > returned something other than 0 (it might return the name of the tag it
> > > jumped to) then you could use the technique to make sure that you jump
> > > backwards at most once. I'm not saying it would be an improvement if
> > > things worked this way but they might and it wouldn't derail the
> > > semantics of go any more than unwind-protect does.
> >
> > Um, I still don't see it. The fundamental problem is that you have to
> > evaluate the argument "(go tag)" to SETQ before you have a value to use
> > to set the variable. But the act of evaulation GO is to transfer
> > control to the TAG. So, before you have a value you can use, you have
> > already moved the execution of the program to another point.
>
> Does this present any bigger problems than unwind-protect ?
Immensely.
UNWIND-PROTECT very explicitly defines the order of execution of the
forms. It guarantees that certain forms WILL be executed in order
regardless of whether the protected statement terminates normally or
through an abnormally (including a transfer of control). So that just
involves specifically saying what happens.
Now, if you want to look into it more deeply, you could consider what
happens when one of the clean-up forms in UNWIND-PROTECT also transfers
control. There is an example in the hyperspec where the consequences
are undefined because of the interaction of control transfer between the
protected clause and the clean-up forms.
> > You can't realistically have any other result for GO. I mean, what are
> > the semantics supposed to be? The GO form returns a value, then has to
> > wait for some other function to finish execution, and THEN transfer
> > control? How would you know exactly where in this process of argument
> > evaluation and function application to stop an insert the jump?
> >
> > What about code like:
> >
> > (let ((v nil)
> > (result 0))
> > (tagbody
> > tryagain
> > (setq v (get-value-from-user "Enter number: "))
> > (setq result (/ 128 (if (zerop v) (go tryagain) v))))
> > result)
> >
> > At what point does the upward percolation of values stop and the
> > transfer of control begin?
>
> Good question and I don't know the answer. Your example shows that it
> would be messy to define precise semantics and if you did it would
> complicate the use of go so things are probably better as they are.
Well, this is why it is difficult, perhaps impossible to actually write
something that could be implemented. At least with UNWIND-PROTECT, you
have specifically stated what forms will get executed and in what
order. Without some syntactic marker to indicate what gets unwound, you
really don't have anything you can do.
Then there is the whole issue of what you would want to do with any
value returned by GO in the first place. If you look at other control
transfer operators, like THROW, they don't return values either. It is
the CATCH that returns a value, even if that value was specified by the
THROW.
> > > But when I first asked the question I was thinking more around the
> > > lines that if you want to use formal methods to prove things about a
> > > programme it would probably make things easier if every form returned a
> > > value.
> >
> > Well, if it makes you feel better, you can pretend that GO returns any
> > value you like. You still have to deal with the problem of the transfer
> > of control. Now, I haven't looked at formal program proof methods in
> > decades, but it seems to me that having any sort of control transfer of
> > this nature will greatly complicate any proofs you might construct.
>
> So you're saying that the transfer of control would present greater
> problems for formal proofs than whether go returns a value or not ,
> yes ?
Sure.
The value it returns is completely irrelevant, because nothing ever gets
to use that value. So it really doesn't matter. The problem for a
formal proof is that you have now to consider how to model and track
this non-local transfer of control or execution flow.
BTW, that's also why GOTO statements have been largely elminated from
most modern programming languages. There was a whole series of articles
debating this in the 1970s or 1980s.
Semantics of go is very well defined. If you change it, that won't be go,
but something else.
I think it would be a function call and tagbody will become labels.
(tagbody
foo (print "foo") (go baz)
bar (print "bar") (go exit)
baz (print "baz") (go bar)
exit)
(labels ((foo () (print "foo") (baz))
(bar () (print "bar") (exit))
(baz () (print "baz") (bar))
(exit ()))
(foo))
Looks and works similar, doesn't it?
The only difference in this case is that function calls might or might not
eat stack
depending on compiler's will, while go never ever eats stack, because it
only does
a control transfer without a need to return back. If you make go returning,
it will
be same as an ordinary function call.
Scheme does not have/need go because in that language tail-call optimization
is guaranteed. If you like it that way more, you can use Scheme instead of
Common Lisp.
If you want a weird hybrid between a function call and goto, use QBASIC.
It has GOSUB construct -- it jumps to a label, but there is RETURN.
http://www.qbasicstation.com/index.php?c=t_adv&t=26
Well, there is no return value, but it is trivial to add it via a variable.
>
> I wouldn't expect purely functional languages to have constructs like
> this, but I'm not very familiar with them. I'm sure one of the Haskell
> folks who like to troll here can answer definitively.
Tecnically Haskell is not a pure functional language. Haskel uses Monads
to accomplish in-order operations.
The dificult thing in Lisp is that without syntax there is no easy way of
seeing if it is a special operator (there are 26..).
The function special-operator-p can be used to quickly determine that.
(special-operator-p- 'g) -> T
--
John Thingstad
If you want to reason about the behavior of Lisp programs, try using a
continuation model. Values are never returned, rather continuation
functions are called with values. The idea of asking what value GO
calls the return continuation with doesn't make any sense -- the whole
point of GO is that it calls a different continuation.
> Lisp could have worked in such a way that a gets modified. If (go tag)
> returned something other than 0 (it might return the name of the tag it
> jumped to) then you could use the technique to make sure that you jump
> backwards at most once. I'm not saying it would be an improvement if
> things worked this way but they might and it wouldn't der
I think I was confused about what you meant. I'd assumed that you
intended GO to transfer control *and* return a value, ie that it would
be some kind of nondeterministic operator. But I think you mean that
GO would do something like: return a value, then jump.
But that is almost as bad, it turns out. Consider something like this code:
(defclass thrower ()
((throwp :initform nil :initarg :throwp
;; should this be THROWER-THROW-P?
:accessor thrower-throwp)
(a :initform nil :initarg :a :reader thrower-a)))
(defmethod (setf thrower-a) (new (thrower thrower))
(with-slots (a throwp) thrower
(if throwp
(throw throwp new)
(setf a new))))
(defmethod does-not-throw-in-cl ((thrower thrower))
(tagbody
(setf (thrower-a thrower) (go end))
end)
thrower)
Now, if GO has the semantics of returning a value then jumping, then if
you pass an appropriate instance of THROWER to DOES-NOT-THROW-IN-CL,
then GO will actually *not* jump, because (SETF THROWER) will have
thrown.
--tim
GO could have the semantics of recording a tranfer, which is the done later.
But then it wouldn't be go. It would be a state machine (and GO probably
would be called something else).
Example:
(state-machine:
:bar
...
:foo
(if condition
(next-state :bar)
...)
Loose semantics: STATE-MACHINE has arguments which are forms
Symbol forms denote state labels, subject to special evaluation rules. Other
forms are ordinary Lisp forms.. The state machine maintains an implicit state
variable. The state variable takes on values which are from the domain of all
of the labels used in the state-machine (such as :foo and :bar) in the above
example. Furthermore, there are two special state values denoting a start and
accept state. These are denoted by the symbols NIL and T. There is an implicit
NIL label before the first form, and an implicit T label at the end. Thus NIL
and T cannot be used as labels.
Before evaluation beings, the hidden state variable is set to the value NIL.
The machine then repeatedly runs the following cycle:
1. If the current state is T, terminate the STATE-MACHINE form.
2. Otherwise, find the label form which matches the current
state, signaling an error if there is no such form or label.
3. Find the label form which follows that form, and set the
current state to that following label.
4. Select the form which immediately follows the form that was found
in step 2.
5. Evaluate the selected form according to these rules:
- If the form is a state label then go to step 1.
- Otherwise, evaluate the form, and upon completion
of that form, select the form which follows the
form that was just evaluated and repeat step 5.
Within the construct, the form (NEXT-STATE <symbol>) changes the current
state to the given value. This is taken into consideration the next time the
algorithm reaches step 1. The values NIL and T may be used; thus,
(next-state t) indicates that the machine will terminate when evaluation
of the current block of forms finishes, and (next-state nil) means
that control will pass to the very beginning (the hidden nil label).
Diagnosis of nonexistent states may be done in the evaluation of NEXT-STATE
rather than in step 2.
> 1. If the current state is T, terminate the STATE-MACHINE form.
> 2. Otherwise, find the label form which matches the current
> state, signaling an error if there is no such form or label.
> 3. Find the label form which follows that form, and set the
> current state to that following label.
> 4. Select the form which immediately follows the form that was found
> in step 2.
> 5. Evaluate the selected form according to these rules:
> - If the form is a state label then go to step 1.
> - Otherwise, evaluate the form, and upon completion
> of that form, select the form which follows the
> form that was just evaluated and repeat step 5.
>
> Within the construct, the form (NEXT-STATE <symbol>) changes the current
> state to the given value. This is taken into consideration the next time the
> algorithm reaches step 1. The values NIL and T may be used; thus,
> (next-state t) indicates that the machine will terminate when evaluation
> of the current block of forms finishes, and (next-state nil) means
> that control will pass to the very beginning (the hidden nil label).
I think that would have more-or-less the behaviour I was thinking of,
which is that a form involving NEXT-STATE might not actually go to the
next state even though NEXT-STATE was evaluated and set the state,
because something else in the form might cause a transfer of control
out of the machine. That would be OK if the next-state thing was
called NEXT-STATE of course, because people would not really expect
that to be GO TO construct. (And actually I can imagine forms in a
state machine which looked like:
(progn
(next-state try-again)
... do stuff...
(when (or ...)
;; ah, actually terminate
(next-state t))
... more ...)
does not change the value of a.
Cheers.