[racket] Tail recursive module cons

From: Yuhao Dong (yd2dong at uwaterloo.ca)
Date: Sat Mar 29 08:45:33 EDT 2014

Thanks. I probably mixed it up also with the implementation of the
compiler targeting C of Chicken Scheme, which basically just converts
everything to continuation-passing style.

I think my point still holds that tail recursion in the case I posted
does not improve performance. At least the timing numbers say so. Of
course, the memory consumption may be larger with the non-tail version,
as I suppose each continuation frame contains more information than a
number.

On Sat, 2014-03-29 at 08:06 -0400, Prabhakar Ragde wrote:
> Yuhao Dong wrote:
> 
> > The thing is, Racket's "stack" is a list of continuations. I don't see
> > how explicitly keeping the "stack" in an accumulator would help. Stack
> > overflow won't be a problem since the "stack" is a list on the heap, and
> > unless memory runs out you wont overflow.
> 
> > I think that tail recursion doesn't help at all, and introduces
> > conceptual overhead. Racket doesn't use the stack, and converts to
> > continuation-passing, which is surprise-surprise *tail recursive* at
> > runtime anyway.
> 
> Yuhao, I think you got some of these ideas from lectures I gave almost 
> exactly a year ago, but you may have forgotten or misunderstood some 
> points I made at the time:
> 
> * Simply making an interpreter tail-recursive does not guarantee that 
> that the interpreted program will use memory wisely. It only ensures 
> that there is no stack overhead due to recursive applications of the 
> interpreter itself (either through tail-call optimization [TCO] in the 
> language the interpreter is written in, or trampolining/looping in a 
> language that does not support TCO).
> 
> * The way in which we made the interpreter tail-recursive, by using a 
> zipper on the AST which introduced a list of continuations to manage 
> control, did ensure that if the interpreted program was tail-recursive, 
> the list of continuations would not grow. We could see this by looking 
> at the interpreter code and noting that continuations grow only when 
> arguments are evaluated down to values and not when a function is 
> applied to a value. However, if the interpreted program is not 
> tail-recursive, the list of continuations will grow, because of the 
> pending computation in the interpreted program. Furthermore, because 
> that list of continuations is on the heap instead of the stack, there 
> will be overhead associated with allocation and garbage collection.
> 
> * The development in lecture was an illustration of one way of achieving 
> the goal of TCO in an imperative implementation that could be understood 
> after six months of exposure to functional programming in Racket. But 
> Racket itself has a more sophisticated implementation that incorporates 
> more advanced ideas and optimizations. It does use the stack as well as 
> the heap, but not in the simple way that a direct translation of the 
> naive (non-tail-recursive) interpreter would.
> 
> I hope that helps clarify things. The lecture material was my attempt at 
> giving you a sense of the ideas behind Racket's implementation, but it 
> was not a description of what Racket actually does under the hood. That 
> would probably take a whole course on its own, and a better teacher. --PR



Posted on the users mailing list.