🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: net.lang.lisp
Article #696

Re: call/cc and proper tail recursion

#696
From: Will Clinger
Date: Wed, 15 Oct 1986 23:38
49 lines
3062 bytes
In article <20000003@uicsrd> harrison@uicsrd.CSRD.UIUC.EDU has a question
about the definition of Scheme as "properly tail-recursive":
>
>	(define f (lambda (x y)
>			(if (f x y) (bad-function))
>			(function-with-side-effects x y)
>			(if (p x y) #!null (f (cdr x) (cdr y))) ))
>
>	This function is tail-recursive.  My understanding is that such
>a function is evaluated with no net growth in the stack.  This means,
>in short, that the parameters x and y will be assigned to (cdr x) and
>(cdr y), and the recursive call effected by jumping to the entrance of
>the function.  Is this so?

Almost.  x and y are bound, not assigned; this distinction is important
because assignment would throw away the old values but binding does not.
By the way, I assume you intended for (f x y) in the first line of the
lambda body to be (g x y) to avoid an infinite (non-tail-recursive) loop.

>	If so, then consider what happens when bad-function is defined
>as
>	(lambda () (set! g (call/cc (lambda (x) x))))
>
>i.e., bad-function records the current state, or continuation, in
>the global variable g, and exits.  Now, at the termination of f,
>g will contain some continuation which, if applied, would mean that
>the bindings of x and y would have to be restored  to a state which occured
>during one of the recursive instances of f.  My question is, how can this
>occur if we don't record these values in a stack frame?

It is best to think of Scheme as a heap-based language instead of a
stack-based language.  In other words, pretend that continuations
(analogous to stack frames) are allocated on a heap and reclaimed by
a garbage collector instead of by adjusting a stack pointer.  Pretend
also that the continuation stored in g was created for the sole purpose
of preserving registers across the call to bad-function, and that those
registers are restored immediately upon return from the call.

The tail-recursive call in the last line of the body doesn't need to
preserve registers, so no continuation is created for it.

If you're wondering how this semantics can be implemented efficiently,
you might start by reading the paper on PC Scheme that was presented at
the 1986 ACM conference on Lisp and functional programming.  That paper
tells how the speed of a stack-based implementation can be achieved
by making call-with-current-continuation an expensive procedure to call.

Will Clinger
Tektronix Computer Research Lab
Technical Details
Message-ID: <740@tekchips.UUCP>
Path: arc1-ca.pugleaf.net!utzoo!mnetor!seismo!cmcl2!yale!husc6!think!nike!ucbcad!ucbvax!hplabs!tektronix!tekcrl!tekchips!willc
References: <20000003@uicsrd>

Show Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: arc1-ca.pugleaf.net!utzoo!mnetor!seismo!cmcl2!yale!husc6!think!nike!ucbcad!ucbvax!hplabs!tektronix!tekcrl!tekchips!willc
From: willc@tekchips.UUCP (Will Clinger)
Newsgroups: net.lang.lisp
Subject: Re: call/cc and proper tail recursion
Message-ID: <740@tekchips.UUCP>
Date: Wed, 15-Oct-86 19:38:11 EDT
Article-I.D.: tekchips.740
Posted: Wed Oct 15 19:38:11 1986
Date-Received: Thu, 16-Oct-86 22:41:40 EDT
References: <20000003@uicsrd>
Reply-To: willc@tekchips.UUCP (Will Clinger)
Organization: Tektronix, Inc., Beaverton, OR.
Lines: 49