Showing posts with label lazy evaluation. Show all posts
Showing posts with label lazy evaluation. Show all posts

Sunday, 24 August 2008

Tail calls don't exist - So why look for them?


import java.util.Stack;

interface Term { public Term whnf(); }
class Function implements Term { public Term whnf() { return this; } public Term apply(Term value) { System.out.println("Not implemented"); return null; } }

class Apply implements Term {
Term x, y;
boolean computed;
Term value;

public Apply(Term x, Term y) {
this.x = x;
this.y = y;
computed = false;
}

/* // This is the first version of whnf I wrote, it's buggy, it crashes with a stack overflow
public Term whnf() {
if(!computed) {
x = x.whnf();
if(x instanceof Constructor) {
value = ((Constructor)x).add(y);
}
else if(x instanceof Function) {
value = ((Function)x).apply(y);
value = value.whnf(); // this is the recursion in question, which causes it
}
else {
System.out.println("ERROR");
}
computed = true;
}
return value;
}*/


public Term whnf() {
while(true) { // the usual fix is to use one of javas many loop constructs instead of recursion
if(!computed) {
x = x.whnf();
if(x instanceof Constructor) {
value = ((Constructor)x).add(y);
}
else if(x instanceof Function) {
value = ((Function)x).apply(y);
if(value instanceof Apply) {
x = ((Apply)value).x;
y = ((Apply)value).y;
continue;
}
}
else {
System.out.println("ERROR");
}
computed = true;
}
return value;
}
}
}

class Constructor implements Term {
String name;
Term[] args;

public Constructor(String name, Term... args) {
this.name = name;
this.args = args;
}

public Term whnf() {
return this;
}

public Constructor add(Term t) {
int i; Term[] oldArgs = args;
args = new Term[oldArgs.length+1];
for(i = 0; i < oldArgs.length; i++)
args[i] = oldArgs[i];
args[i] = t;
return this;
}
}

class Add extends Function { public Term apply(Term value) { return new Add1(value); } }
class Add1 extends Function {
Term x;
public Add1(Term x) { this.x = x; }
public Term apply(Term y) {
x = x.whnf();
if(((Constructor)x).name == "Z") { return y; } // add Z y = y
else if(((Constructor)x).name == "S") { // add (S x) y = S (add x y)
return new Constructor("S", new Apply(new Apply(new Add(), ((Constructor)x).args[0]), y));
}
System.out.println("Pattern match fell through");
return null;
}
}

class Tail extends Function { public Term apply(Term value) { return ((Constructor)value.whnf()).args[1]; } }

class ZipWith extends Function { public Term apply(Term f) { return new ZipWith1(f); } }
class ZipWith1 extends Function { Term f; public ZipWith1(Term f) { this.f = f; } public Term apply(Term xs) { return new ZipWith2(f,xs); } }
class ZipWith2 extends Function {
Term f, xs;
public ZipWith2(Term f, Term xs) { this.f = f; this.xs = xs; }
public Term apply(Term ys) {
xs = xs.whnf();
if(((Constructor)xs).name == "[]") return ys; // zipWith f [] y = y
ys = ys.whnf();
if(((Constructor)ys).name == "[]") return xs; // zipWith f x [] = x
Term x = ((Constructor)xs).args[0]; Term y = ((Constructor)ys).args[0];
Term xss = ((Constructor)xs).args[1]; Term yss = ((Constructor)ys).args[1];
// zipWith f (x:xss) (y:yss) = f x y : zipWith f xss yss
return new Constructor(":", new Apply(new Apply(f,x), y), new Apply(new Apply(new Apply(new ZipWith(), f), xss), yss));
}
}

class F extends Function { public Term apply(Term x) { return new Apply(new F(), x); } }

interface Instruction { public void execute(Stack<Instruction> todo); }

class OpenBracket implements Instruction {
public void execute(Stack<Instruction> todo) {
System.out.print("(");
}
}

class CloseBracket implements Instruction {
public void execute(Stack<Instruction> todo) {
System.out.print(")");
}
}

class EvalTerm implements Instruction {
Term t;

public EvalTerm(Term t) {
this.t = t;
}

public void execute(Stack<Instruction> todo) {
Constructor c = (Constructor)t.whnf();
t = c;

System.out.print(c.name + " ");

todo.push(new CloseBracket());
for(int i = c.args.length-1; i >= 0; i--) {
todo.push(new EvalTerm(c.args[i]));
}
todo.push(new OpenBracket());
}
}

class Evaluator {
Stack<Instruction> todo;

public Evaluator(Term program) {
todo = new Stack<Instruction>();
todo.push(new EvalTerm(program));
}

public void evaluate() {
while(!todo.empty()) todo.pop().execute(todo);
}
}

public class NoTCO {
public static void main(String args[]) {
Term one = new Apply(new Constructor("S"), new Constructor("Z"));
Term two = new Apply(new Constructor("S"), new Apply(new Constructor("S"), new Constructor("Z")));
Term four = new Apply(new Constructor("S"), new Apply(new Constructor("S"), new Apply(new Constructor("S"), new Apply(new Constructor("S"), new Constructor("Z")))));

// 1 : 1 : zipWith (+) fibs (tail fibs)
Term fibs =
new Apply(new Apply(new Constructor(":"), one),
new Apply(new Apply(new Constructor(":"), one),
null));
((Apply)((Apply)fibs).y).y =
new Apply(new Apply(new Apply(new ZipWith(), new Add()), fibs), new Apply(new Tail(), fibs));

//new Evaluator(fibs).evaluate();
new Evaluator(new Apply(new F(), new Constructor("3"))).evaluate();
System.out.println();
}
}


// Important thing to note is,
// 1) This code does not analyze the input program to detect and 'optimize' tail recursion
// 2) This program doesn't stack overflow when evaluating a "tail recursive" program
//
// NB. I put "tail recursive" in quotes because it's actually irrelevant that the code is what you'd call in a strict language by that term
// as I said in my previous post, the reason it doesn't stack overflow is not at all related to that.