Newsgroups: comp.lang.scheme
Path: cantaloupe.srv.cs.cmu.edu!das-news.harvard.edu!news2.near.net!MathWorks.Com!europa.eng.gtefsd.com!howland.reston.ans.net!vixen.cso.uiuc.edu!csrd.uiuc.edu!sp10.csrd.uiuc.edu!harrison
From: harrison@sp10.csrd.uiuc.edu (Luddy Harrison)
Subject: Re: Scheme _in_ Emacs?
In-Reply-To: blume@beth.princeton.edu's message of Mon, 5 Sep 1994 15:02:32 GMT
Message-ID: <HARRISON.94Sep5193413@sp10.csrd.uiuc.edu>
Followup-To: comp.lang.scheme
Sender: news@csrd.uiuc.edu
Organization: UIUC Center for Supercomputing Research and Development
References: <RAMSDELL.94Aug17063434@triad.mitre.org> <TTHORN.94Aug17150829@ceres.daimi>
	<RAMSDELL.94Aug24085039@triad.mitre.org>
	<WGD.94Aug25012615@martigny.ai.mit.edu>
	<TFB.94Aug31020302@oliphant.cogsci.ed.ac.uk>
	<WGD.94Sep3205634@martigny.ai.mit.edu>
	<TFB.94Sep5132531@burns.cogsci.ed.ac.uk>
	<HARRISON.94Sep5085451@sp10.csrd.uiuc.edu>
	<BLUME.94Sep5110232@beth.princeton.edu>
Date: 06 Sep 1994 00:34:13 GMT
Lines: 80

>>In a dynamically scoped language *all* local variables might possibly
>>be subject to access from another function, and there is nothing a
>>compiler can predict about it (in general).

>> ... discussion of tail-recursion optimization elided ...

I thought that dynamically bound vars had to be so declared, by a form
like (defvar *bar* ...), or whatever.

In any event my point was this.  When the original poster writes:

   >>>You can't optimize a tail call
   >>>by throwing away the stack frame of the function because the function
   >>>you call can legitimately refer to the bindings you have made.

he seems to suggest that the problem is with dynamic binding; but the
same problem occurs (can't deallocate the caller's stack frame on a
tail-recursive call) any time that data allocated in a caller's
activation is needed by a callee.  In Scheme this arises when
variables are captured by closure and passed to the callee; to solve
it, variables captured by closure are put in the heap.  If dynamically
bound variables were similarly allocated on the heap (e.g., an alist
representing the dynamic environment were passed from caller to
callee), then dynamic binding would likewise not conflict with
tail-recursion optimization.  In summary: the problem is with
stack-allocation of data, not with dynamic binding.

Now, the second question is whether proper tail recursion is a
meaningful mandate.

	>> The implementation must implement tail-recursion in a
	>> way that let's it run in constant space.  Of course,
	>> this ignores the fact that the recursion (tail or not)
	>> can grow some data structure as it passes it on and on
	>> through the procedure arguments: ...
	>> The point is that the data-structures implicitly introduced by the
	>> compiler or the interpreter in order to manage the control flow must
	>> not grow infinitely, in fact, they must be bounded by a constant.

Is it really so easy to divide the data into "that which is introduced
implicitly to manage the control-flow" and <all the other data>?  If
we consider the old example

	(define fact (lambda (n k)
           (if (= n 1)
               (k 1)
               (fact (- n 1) (lambda (m) (k (* n m)))))))

        (fact N)

there are lots of implementation choices.

A) We could do the usual thing, i.e., CPS conversion and heap-storage for 
closures.

B) We could stack the activation records and implement the inner lambda
like a nested function in Pascal.

C) (Being very fancy:) we could recognize the underlying recurrence relation
   x[n] = x[n-1]*n
   x[1] = 1
   and build a parallel computation that solved the problem in log(N) space,
   and log(N) time.

D) we could look the answer up in a table of all the
<(Scheme programs) X (input data)> that have ever been run on our system,
and record the outcome of this computation in the table if it is not found.

and so on ad infinitum.

Which of these are permitted by the standard?  The way I read the
quotation above, (B) is forbidden (it uses O(N) space for control
information), even though it would probably be faster and consume less
space overall than (A).  Suppose that (C) requires log(N) space for
control-flow information; (A), by definition, requires only constant
space for control-flow information.  Is (C) therefore forbidden?  (D)
uses a zillion terabytes of information but none of it for
control-flow.  Is (D) therefore OK?

-Luddy Harrison
