<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.jvmlangsummit.com/index.php?action=history&amp;feed=atom&amp;title=Why_Tailcalls_%28GLS%29</id>
	<title>Why Tailcalls (GLS) - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.jvmlangsummit.com/index.php?action=history&amp;feed=atom&amp;title=Why_Tailcalls_%28GLS%29"/>
	<link rel="alternate" type="text/html" href="https://wiki.jvmlangsummit.com/index.php?title=Why_Tailcalls_(GLS)&amp;action=history"/>
	<updated>2026-05-23T01:48:42Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.32.0</generator>
	<entry>
		<id>https://wiki.jvmlangsummit.com/index.php?title=Why_Tailcalls_(GLS)&amp;diff=747&amp;oldid=prev</id>
		<title>Jrose: New page: While searching for something else, I happened to stumble across an old proposal for JVM tail calls from 1998 (updated 2002)!  Since Cameron apparently mentioned them at the JVM summit, I ...</title>
		<link rel="alternate" type="text/html" href="https://wiki.jvmlangsummit.com/index.php?title=Why_Tailcalls_(GLS)&amp;diff=747&amp;oldid=prev"/>
		<updated>2011-07-20T21:02:39Z</updated>

		<summary type="html">&lt;p&gt;New page: While searching for something else, I happened to stumble across an old proposal for JVM tail calls from 1998 (updated 2002)!  Since Cameron apparently mentioned them at the JVM summit, I ...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;While searching for something else, I happened to stumble across an old proposal&lt;br /&gt;
for JVM tail calls from 1998 (updated 2002)!  Since Cameron apparently mentioned&lt;br /&gt;
them at the JVM summit, I couldn't resist sending this around for your amusement.&lt;br /&gt;
&lt;br /&gt;
--Guy&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
Proposal for JVM tail call support&lt;br /&gt;
&lt;br /&gt;
Guy Steele, May 15, 1998&lt;br /&gt;
updated January 3, 2002&lt;br /&gt;
updated January 8, 2002&lt;br /&gt;
&lt;br /&gt;
Summary: phase in support for tail calls in a manner that is&lt;br /&gt;
compatible with existing JVM implementations (except that such&lt;br /&gt;
implementations might run out of storage when executing code that&lt;br /&gt;
relies heavily on tail calls).&lt;br /&gt;
&lt;br /&gt;
This is done by adding a &amp;quot;TailCall&amp;quot; attribute to methods in class files.&lt;br /&gt;
&lt;br /&gt;
A &amp;quot;goto&amp;quot; statement is added to the Java programming language to allow&lt;br /&gt;
tail calls to be expressed in source code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(1) At the JVM level:&lt;br /&gt;
&lt;br /&gt;
Introduce a new attribute of the method_info structure: the &amp;quot;TailCall&amp;quot;&lt;br /&gt;
attribute.  The info of this attribute is a list of pc locations&lt;br /&gt;
within the Code attribute for the same method.  Each pc location must&lt;br /&gt;
be the location of an invokeinterface, invokespecial, invokestatic, or&lt;br /&gt;
invokevirtual instruction; moreover, that instruction must be&lt;br /&gt;
immediately followed by a return instruction.  If these constraints&lt;br /&gt;
are not met, the class file is considered malformed and may be&lt;br /&gt;
rejected by a JVM.&lt;br /&gt;
&lt;br /&gt;
An invokeinterface, invokespecial, invokestatic, or invokevirtual&lt;br /&gt;
instruction that is identified by the TailCall attribute is intended&lt;br /&gt;
to be executed as a tail call if possible.  This is done by first&lt;br /&gt;
following all the usual rules for locating the method to be invoked,&lt;br /&gt;
and then (in effect) popping the arguments (and perhaps an objectref)&lt;br /&gt;
from the stack.  At this point, a test is made to see whether a tail&lt;br /&gt;
call is permitted (see below).  If the test succeeds, then the current&lt;br /&gt;
stack frame is removed from the stack as if by an appropriate return&lt;br /&gt;
instruction; this also restores the PC to a point within the *caller*&lt;br /&gt;
of the current method.  Then, whether the test succeeded or failed,&lt;br /&gt;
the rest of the invoke operation is carried out by creating a new&lt;br /&gt;
stack frame, installing the arguments (and perhaps an objectref) as&lt;br /&gt;
local variables, etc.&lt;br /&gt;
&lt;br /&gt;
Here is the test mentioned in the preceding paragraph:&lt;br /&gt;
&lt;br /&gt;
 (1) The test is permitted to fail or to succeed, at the&lt;br /&gt;
 discretion of the implementation, if the called method is native.&lt;br /&gt;
 It is the responsibility of the implementation to ensure that security&lt;br /&gt;
 policies are not compromised if a tail call is used.&lt;br /&gt;
&lt;br /&gt;
 (2) Otherwise, the test is permitted to fail or to succeed, at the&lt;br /&gt;
 discretion of the implementation, if the class loader of either the&lt;br /&gt;
 calling method or the called method is the system class loader (null).&lt;br /&gt;
 It is the responsibility of the implementation to ensure that security&lt;br /&gt;
 policies are not compromised if a tail call is used.&lt;br /&gt;
&lt;br /&gt;
 (3) Otherwise, the test definitely fails if the class loader of the&lt;br /&gt;
 calling method and the class loader of the called method are different&lt;br /&gt;
 and neither is the system class loader (null).&lt;br /&gt;
&lt;br /&gt;
 (4) Otherwise, the test definitely fails if the protection domain of the&lt;br /&gt;
 calling method and the protection domain of the called method are different.&lt;br /&gt;
&lt;br /&gt;
 (5) Otherwise, the test definitely succeeds.&lt;br /&gt;
&lt;br /&gt;
Thus, an application programmer can rely on a tail call consuming no&lt;br /&gt;
net stack space if the JVM supports tail calls and the called method&lt;br /&gt;
has the same class loader and protection domain as the calling method.&lt;br /&gt;
An important special case is that tail calls always &amp;quot;work&amp;quot; if the JVM&lt;br /&gt;
supports tail calls and the called method is defined in the same class&lt;br /&gt;
as the calling method.&lt;br /&gt;
&lt;br /&gt;
It is permitted, though discouraged, for a JVM to ignore the TailCall&lt;br /&gt;
attribute.  In this case, code will be executed in a semantically&lt;br /&gt;
equivalent manner but may fail to complete execution if it runs out of&lt;br /&gt;
storage for stack frames.  (This is permitted primarily to allow some&lt;br /&gt;
measure of compatibility with existing JVM implementations.  The&lt;br /&gt;
intent, however, is to phase in implementations that all properly&lt;br /&gt;
support tail calls so that, after a certain point in time, programmers&lt;br /&gt;
will be able to rely on it as a programming mechanism.)&lt;br /&gt;
&lt;br /&gt;
(This is exactly analogous to the observation that it is permitted,&lt;br /&gt;
but discouraged, for a JVM not to have a garbage collector; it could&lt;br /&gt;
just allocate new objects out of a finite pool of storage and then&lt;br /&gt;
give up when the pool is exhausted.  But that's not considered a&lt;br /&gt;
quality implementation.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(2) At the JLS level:&lt;br /&gt;
&lt;br /&gt;
Introduce a new statement:&lt;br /&gt;
&lt;br /&gt;
	goto &amp;lt;MethodInvocation&amp;gt; ;&lt;br /&gt;
&lt;br /&gt;
This may appear in exactly the places that a return statement may appear.&lt;br /&gt;
(Perhaps, out of an abundance of caution, we should not allow it&lt;br /&gt;
to be used within a constructor.)&lt;br /&gt;
&lt;br /&gt;
If the type of the &amp;lt;MethodInvocation&amp;gt; expression is void, then this&lt;br /&gt;
statement may appear within the body of a method whose return type is&lt;br /&gt;
void [or within the body of a constructor?].  This statement is then&lt;br /&gt;
semantically equivalent to&lt;br /&gt;
&lt;br /&gt;
	{ &amp;lt;MethodInvocation&amp;gt;; return; }&lt;br /&gt;
&lt;br /&gt;
If the type of the &amp;lt;MethodInvocation&amp;gt; expression is not void, then&lt;br /&gt;
this statement may appear within the body of a method whose return&lt;br /&gt;
type is the same as the type of the &amp;lt;MethodInvocation&amp;gt; expression.&lt;br /&gt;
This statement is then semantically equivalent to&lt;br /&gt;
&lt;br /&gt;
	return &amp;lt;MethodInvocation&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
In each case, however, the &amp;quot;goto&amp;quot; form additionally specifies a&lt;br /&gt;
requirement, or at least the strong desire, that the implementation&lt;br /&gt;
should recycle the stack frame of the currently running method&lt;br /&gt;
as it performs the invocation of the called method.  The intent&lt;br /&gt;
is that it should be possible to execute an indefinitely large&lt;br /&gt;
number of &amp;quot;goto&amp;quot; calls without running out of space by reason&lt;br /&gt;
of stack overflow.&lt;br /&gt;
&lt;br /&gt;
However, the programmer cannot expect the stack frame to be recycled&lt;br /&gt;
in this manner if the call might fail the test (mentioned above in the&lt;br /&gt;
JVM section).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(3) Small example: Searching a polymorphic list&lt;br /&gt;
&lt;br /&gt;
Suppose there is a list with two or more kinds of nodes, and we want&lt;br /&gt;
to ask whether there is a node in the list that matches a given&lt;br /&gt;
string, and if so, return an associated value.  The &amp;quot;obvious&amp;quot; way to&lt;br /&gt;
do it is as follows:&lt;br /&gt;
&lt;br /&gt;
 Interface Chain { Value lookup(String name); }&lt;br /&gt;
&lt;br /&gt;
 class SimplePair implements Chain {&lt;br /&gt;
  String name;&lt;br /&gt;
  Value val;&lt;br /&gt;
  Chain next;&lt;br /&gt;
  ...&lt;br /&gt;
  Value lookup(String query) {&lt;br /&gt;
    if (name.equals(query)) return value;&lt;br /&gt;
    if (next == null) return null;&lt;br /&gt;
    return next.lookup(query);		;not me---pass the buck&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class ComputedValue implements Chain {&lt;br /&gt;
  String oldprefix;&lt;br /&gt;
  String newprefix;&lt;br /&gt;
  Chain next;&lt;br /&gt;
  ...&lt;br /&gt;
  Value lookup(String query) {&lt;br /&gt;
    if (query.startsWith(prefix))&lt;br /&gt;
      return new Value(newprefix + query.substring(prefix.length()));&lt;br /&gt;
    if (next == null) return null;&lt;br /&gt;
    return next.lookup(query);		;not me---pass the buck&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Unfortunately, in current JVM implementations, this chews up a stack&lt;br /&gt;
frame for every node in the chain.  As a result, implementors often try&lt;br /&gt;
to use an explicit while loop, which results in tearing apart the code&lt;br /&gt;
for the lookup method in complicated ways.&lt;br /&gt;
&lt;br /&gt;
With tail calls, just replace&lt;br /&gt;
&lt;br /&gt;
   return next.lookup(query);&lt;br /&gt;
&lt;br /&gt;
with&lt;br /&gt;
&lt;br /&gt;
   goto next.lookup(query);&lt;br /&gt;
&lt;br /&gt;
and both the algorithmic intent and the intended stack behavior are&lt;br /&gt;
quite clear.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(4) Big example: A tiny Lisp evaluator&lt;br /&gt;
&lt;br /&gt;
Here is an interpreter for a tiny lambda-calculus-based language that&lt;br /&gt;
consists of numeric (BigInteger) literals, variables, an if-then-else&lt;br /&gt;
expression, lambda expressions of one parameter, a rec (label)&lt;br /&gt;
construct for naming a recursive function, and function calls with one&lt;br /&gt;
argument.  It provides add and multiply operators in curried form, so&lt;br /&gt;
that one must say ((+ 3) 4), for example.  There is also an explicit&lt;br /&gt;
zero test primitive that returns true or false.  (Internally, the&lt;br /&gt;
number 0 is used as the false value for if-then-else.)&lt;br /&gt;
&lt;br /&gt;
Programs are of type Expression; the eval method produces a Value.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 interface Expression {&lt;br /&gt;
  Value eval(Environment env);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class LiteralNode implements Expression {&lt;br /&gt;
  final Value item;&lt;br /&gt;
  LiteralNode(Value item) { this.item = item; }&lt;br /&gt;
  public Value eval(Environment env) { return item; }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class VariableNode implements Expression {&lt;br /&gt;
  final String name;&lt;br /&gt;
  VariableNode(String name) { this.name = name; }&lt;br /&gt;
  public Value eval(Environment env) { return env.lookup(name); }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class IfNode implements Expression {&lt;br /&gt;
  final Expression test, thenpart, elsepart;&lt;br /&gt;
  IfNode(Expression test, Expression thenpart, Expression elsepart) {&lt;br /&gt;
    this.test = test; this.thenpart = thenpart; this.elsepart = elsepart;&lt;br /&gt;
  }&lt;br /&gt;
  public Value eval(Environment env) {&lt;br /&gt;
    if (!(test.eval(env).isZero()))&lt;br /&gt;
      goto thenpart.eval(env);&lt;br /&gt;
    else&lt;br /&gt;
      goto elsepart.eval(env);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class LambdaNode implements Expression {&lt;br /&gt;
  final String name;&lt;br /&gt;
  final Expression body;&lt;br /&gt;
  LambdaNode(String name, Expression body) { this.name = name; this.body = body; }&lt;br /&gt;
  public Value eval(Environment env) { return new Closure(name, body, env); }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class RecNode implements Expression {&lt;br /&gt;
  final String name;&lt;br /&gt;
  final Expression body;&lt;br /&gt;
  RecNode(String name, Expression body) { this.name = name; this.body = body; }&lt;br /&gt;
  public Value eval(Environment env) {&lt;br /&gt;
    Environment newenv = env.push(name, null);&lt;br /&gt;
    Value item = body.eval(newenv);&lt;br /&gt;
    newenv.clobber(item);&lt;br /&gt;
    return item;&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class CallNode implements Expression {&lt;br /&gt;
  final Expression fn, arg;&lt;br /&gt;
  CallNode(Expression fn, Expression arg) { this.fn = fn; this.arg = arg; }&lt;br /&gt;
  public Value eval(Environment env) { goto fn.eval(env).invoke(arg.eval(env)); }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 interface Value {&lt;br /&gt;
  Value invoke(Value arg);&lt;br /&gt;
  boolean isZero();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class IntVal implements Value {&lt;br /&gt;
  final java.math.BigInteger v;&lt;br /&gt;
  IntVal(java.math.BigInteger v) { this.v = v; }&lt;br /&gt;
  IntVal(int v) { this.v = java.math.BigInteger.valueOf(v); }&lt;br /&gt;
  static java.math.BigInteger zero = java.math.BigInteger.valueOf(0);&lt;br /&gt;
  static java.math.BigInteger one = java.math.BigInteger.valueOf(1);&lt;br /&gt;
  public boolean isZero() { return v.equals(zero); }&lt;br /&gt;
  Value zeroTest() { return new IntVal(isZero() ? one : zero); }&lt;br /&gt;
  Value add(Value that) { return new IntVal(this.v.add(((IntVal)that).v)) ; }&lt;br /&gt;
  Value multiply(Value that) { return new IntVal(this.v.multiply(((IntVal)that).v)) ; }&lt;br /&gt;
  public Value invoke(Value arg) { throw new Error(&amp;quot;Can't invoke an integer&amp;quot;); }&lt;br /&gt;
  public String toString() { return v.toString(); }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 abstract class NonZeroValue implements Value { public boolean isZero() { return false; } }&lt;br /&gt;
&lt;br /&gt;
 class Closure extends NonZeroValue {&lt;br /&gt;
  final String name;&lt;br /&gt;
  final Expression body;&lt;br /&gt;
  final Environment env;&lt;br /&gt;
  Closure(String name, Expression body, Environment env) {&lt;br /&gt;
    this.name = name; this.body = body; this.env = env;&lt;br /&gt;
  }&lt;br /&gt;
  public Value invoke(Value arg) { goto body.eval(env.push(name, arg)); }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class ZeroTestPrimitive extends NonZeroValue {&lt;br /&gt;
  public Value invoke(Value arg) { return ((IntVal)arg).zeroTest(); }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class AddPrimitive extends NonZeroValue {&lt;br /&gt;
  public Value invoke(Value arg) { return new CurriedAdd(arg); }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class CurriedAdd extends NonZeroValue {&lt;br /&gt;
  final Value arg1;&lt;br /&gt;
  CurriedAdd(Value arg1) { this.arg1 = arg1; }&lt;br /&gt;
  public Value invoke(Value arg2) {&lt;br /&gt;
    return ((IntVal)arg1).add(arg2);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class MultPrimitive extends NonZeroValue {&lt;br /&gt;
  public Value invoke(Value arg) { return new CurriedMult(arg); }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class CurriedMult extends NonZeroValue {&lt;br /&gt;
  final Value arg1;&lt;br /&gt;
  CurriedMult(Value arg1) { this.arg1 = arg1; }&lt;br /&gt;
  public Value invoke(Value arg2) {&lt;br /&gt;
    return ((IntVal)arg1).multiply(arg2);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 class Environment {&lt;br /&gt;
  final String name;&lt;br /&gt;
  Value item;&lt;br /&gt;
  final Environment next;&lt;br /&gt;
  static Environment empty = new Environment(null, null, null);&lt;br /&gt;
  private Environment(String name, Value item, Environment next) {&lt;br /&gt;
    this.name = name; this.item = item; this.next = next;&lt;br /&gt;
  }&lt;br /&gt;
  Environment push(String name, Value item) {&lt;br /&gt;
    return new Environment(name, item, this);&lt;br /&gt;
  }&lt;br /&gt;
  void clobber(Value item) { this.item = item; }&lt;br /&gt;
  Value lookup(String query) {&lt;br /&gt;
    if (this == empty) throw new Error(&amp;quot;Name &amp;quot; + query + &amp;quot; not found&amp;quot;);&lt;br /&gt;
    if (name.equals(query)) return item;&lt;br /&gt;
    goto next.lookup(query);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 /*&lt;br /&gt;
 Here is test code that executes the expression&lt;br /&gt;
&lt;br /&gt;
  ((rec fact (lambda (n) (if ((= n) 0) 1 ((* n) (fact ((+ n) -1)))))) j)&lt;br /&gt;
&lt;br /&gt;
 to compute the factorial function for various values of j.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
 public class Foo {&lt;br /&gt;
  static Expression demo(int j) {&lt;br /&gt;
    return new CallNode(&lt;br /&gt;
              new RecNode(&amp;quot;fact&amp;quot;,&lt;br /&gt;
                new LambdaNode(&amp;quot;n&amp;quot;,&lt;br /&gt;
                  new IfNode(&lt;br /&gt;
                    new CallNode(&lt;br /&gt;
                      new LiteralNode(new ZeroTestPrimitive()),&lt;br /&gt;
                     new VariableNode(&amp;quot;n&amp;quot;)&lt;br /&gt;
                   ),&lt;br /&gt;
                    new LiteralNode(new IntVal(1)),&lt;br /&gt;
                    new CallNode(&lt;br /&gt;
                      new CallNode(&lt;br /&gt;
                        new LiteralNode(new MultPrimitive()),&lt;br /&gt;
                       new VariableNode(&amp;quot;n&amp;quot;)&lt;br /&gt;
                     ),&lt;br /&gt;
                      new CallNode(&lt;br /&gt;
                        new VariableNode(&amp;quot;fact&amp;quot;),&lt;br /&gt;
                        new CallNode(&lt;br /&gt;
                          new CallNode(&lt;br /&gt;
                           new LiteralNode(new AddPrimitive()),&lt;br /&gt;
                           new VariableNode(&amp;quot;n&amp;quot;)&lt;br /&gt;
                         ),&lt;br /&gt;
                          new LiteralNode(new IntVal(-1))&lt;br /&gt;
                        )&lt;br /&gt;
                      )&lt;br /&gt;
                    )&lt;br /&gt;
                  )&lt;br /&gt;
                )&lt;br /&gt;
              ),&lt;br /&gt;
              new LiteralNode(new IntVal(j)));&lt;br /&gt;
  }&lt;br /&gt;
  public static void main(String args[]) {&lt;br /&gt;
    System.out.println(demo(0).eval(Environment.empty).toString());&lt;br /&gt;
    System.out.println(demo(5).eval(Environment.empty).toString());&lt;br /&gt;
    System.out.println(demo(8).eval(Environment.empty).toString());&lt;br /&gt;
    System.out.println(demo(1000).eval(Environment.empty).toString());&lt;br /&gt;
    System.out.println(demo(1000000).eval(Environment.empty).toString());&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Under the existing JVM, I tested the code exactly as shown except that&lt;br /&gt;
I replaced the keyword &amp;quot;goto&amp;quot; with the keyword &amp;quot;return&amp;quot;.  Here is the&lt;br /&gt;
output:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 livia 69 =&amp;gt;java Foo&lt;br /&gt;
 1&lt;br /&gt;
 24&lt;br /&gt;
 120&lt;br /&gt;
 40320&lt;br /&gt;
 402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000&lt;br /&gt;
 Exception in thread &amp;quot;main&amp;quot; java.lang.StackOverflowError: c stack overflow&lt;br /&gt;
 livia 70 =&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note that it correctly computed all the factorial values but the last,&lt;br /&gt;
and ran out of stack trying to compute the last one---and rightly so,&lt;br /&gt;
ha ha, for this computation is not tail-recursive!  But now consider&lt;br /&gt;
this test code, which performs a factorial computation tail-recursively:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 /*&lt;br /&gt;
 Here is test code that executes the expression&lt;br /&gt;
&lt;br /&gt;
  (((rec fact (lambda (n) (lambda (a) (if ((= n) 0) a ((fact ((+ n) -1)) ((* n) a)))))) j) 1)&lt;br /&gt;
&lt;br /&gt;
 to compute the factorial function for various values of j.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
 public class Bar {&lt;br /&gt;
  static Expression demo(int j) {&lt;br /&gt;
    return new CallNode(&lt;br /&gt;
             new CallNode(&lt;br /&gt;
                new RecNode(&amp;quot;fact&amp;quot;,&lt;br /&gt;
                  new LambdaNode(&amp;quot;n&amp;quot;,&lt;br /&gt;
                    new LambdaNode(&amp;quot;a&amp;quot;,&lt;br /&gt;
                      new IfNode(&lt;br /&gt;
                        new CallNode(&lt;br /&gt;
                          new CallNode(&lt;br /&gt;
                            new LiteralNode(new EqualsPrimitive()),&lt;br /&gt;
                            new VariableNode(&amp;quot;n&amp;quot;)&lt;br /&gt;
                          ),&lt;br /&gt;
                          new LiteralNode(new IntVal(0))&lt;br /&gt;
                        ),&lt;br /&gt;
                        new VariableNode(&amp;quot;a&amp;quot;),&lt;br /&gt;
                        new CallNode(&lt;br /&gt;
                          new CallNode(&lt;br /&gt;
                            new VariableNode(&amp;quot;fact&amp;quot;),&lt;br /&gt;
                            new CallNode(&lt;br /&gt;
                              new CallNode(&lt;br /&gt;
                                new LiteralNode(new AddPrimitive()),&lt;br /&gt;
                                new VariableNode(&amp;quot;n&amp;quot;)&lt;br /&gt;
                              ),&lt;br /&gt;
                              new LiteralNode(new IntVal(-1))&lt;br /&gt;
                            )&lt;br /&gt;
                          ),&lt;br /&gt;
                          new CallNode(&lt;br /&gt;
                            new CallNode(&lt;br /&gt;
                              new LiteralNode(new MultPrimitive()),&lt;br /&gt;
                              new VariableNode(&amp;quot;n&amp;quot;)&lt;br /&gt;
                            ),&lt;br /&gt;
                            new VariableNode(&amp;quot;a&amp;quot;)&lt;br /&gt;
                          )&lt;br /&gt;
                        )&lt;br /&gt;
                      )&lt;br /&gt;
                   )&lt;br /&gt;
                  )&lt;br /&gt;
                ),&lt;br /&gt;
                new LiteralNode(new IntVal(j))),&lt;br /&gt;
              new LiteralNode(new IntVal(1)));&lt;br /&gt;
  }&lt;br /&gt;
  public static void main(String args[]) {&lt;br /&gt;
    System.out.println(demo(0).eval(Environment.empty).toString());&lt;br /&gt;
    System.out.println(demo(5).eval(Environment.empty).toString());&lt;br /&gt;
    System.out.println(demo(8).eval(Environment.empty).toString());&lt;br /&gt;
    System.out.println(demo(1000).eval(Environment.empty).toString());&lt;br /&gt;
    System.out.println(demo(1000000).eval(Environment.empty).toString());&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This, actually, uses up stack less quickly and so runs out of memory trying&lt;br /&gt;
to compute really huge BigIntegers.  So I changed the starting value 1 to 0,&lt;br /&gt;
and changed * to +, so that it computes triangular numbers instead of&lt;br /&gt;
factorials, and got this output:&lt;br /&gt;
&lt;br /&gt;
 livia 87 =&amp;gt;java Bar&lt;br /&gt;
 0&lt;br /&gt;
 15&lt;br /&gt;
 36&lt;br /&gt;
 500500&lt;br /&gt;
 Exception in thread &amp;quot;main&amp;quot; java.lang.StackOverflowError: c stack overflow&lt;br /&gt;
 livia 88 =&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
and I claim that that last stack overflow need not occur if only&lt;br /&gt;
tail calls were supported in Java.&lt;br /&gt;
&lt;br /&gt;
In the code for the evaluator, note how the statements&lt;br /&gt;
&lt;br /&gt;
     goto thenpart.eval(env);&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
     goto elsepart.eval(env);&lt;br /&gt;
&lt;br /&gt;
in the code for the eval method of IfNode make clear the intent&lt;br /&gt;
to process the chosen part of an &amp;quot;if&amp;quot; expression tail-recursively.&lt;br /&gt;
Similarly for the invocation of a function by a CallNode and the&lt;br /&gt;
evaluation of a body by a Closure.&lt;/div&gt;</summary>
		<author><name>Jrose</name></author>
		
	</entry>
</feed>