Implement functional-programming style Cons cells in Java. Provide six implementations.The implementations should have the following properties:
All six implementations should implement the toString method of java.lang.Object as well as the following interface:
public interface Cons {
Object car();
Cons cdr();
};
Mutable interfaces should also implement the following interface:
public interface MutableCons extends Cons {
void setCar(Object) throws ClassCastException;
void setCdr(MutableCons) throws ClassCastException;
};
You will need to throw a new instance of ClassCastException if the types of arguments are inconsistent with the types of other parts of the Cons (depending on what kind of Cons cell you are implementing). You will also need to be familiar with the methods of java.lang.Class, to show cells and to implement type homogeneity constraints.
A base class for immutable Cons cells. We add the type checking on top of this.
public class ICons implements Cons {
private final Object item;
private final IUCons rest;
protected ICons(Object ncar, IUCons ncdr) { item=ncar; rest=ncdr; };
public Object car() { return item; };
public Cons cdr() { return rest; };
};
Question to the audience: Why is the constructor protected?
Since the ICons base class doesn't do anything funny with types, the untyped class doesn't need to either.
public class IUCons extends ICons {
public IUCons(Object ncar, IUCons ncdr) { super(ncar, ncdr); };
};
public class ISCons extends ICons {
public ISCons(Object ncar, IUCons ncdr) {
if (ncdr) {
Class check = ncdr.car().getClass();
if (check == ncar.getClass()) {
super(ncar, ncdr);
} else {
throw new ClassCastException("Inconsistent type");
}
} else {
super(ncar, ncdr);
}
};
};
public class INPCons extends ICons {
public INPCons(Object ncar, IPCons ncdr) {
if (ncdr) {
Class check = ncdr.car().getClass();
if (check.instanceOf(ncar)) {
super(ncar, ncdr);
} else {
throw new ClassCastException("Inconsistent type");
}
} else {
super(ncar, ncdr);
}
};
};
public class IBPCons extends ICons {
protected final Class kind;
public IBPCons(Object ncar, IPCons ncdr) {
if (ncdr) {
if (ncdr.kind.instanceOf(ncar)) {
super(ncar, ncdr);
} else {
throw new ClassCastException("Inconsistent type");
}
} else {
kind = ncar.getClass();
super(ncar, ncdr);
}
};
};
A base class for mutable Cons cells. We add the type checking on top of this.
public class MCons implements MutableCons {
private Object item;
private MCons rest;
protected MCons(Object ncar, IUCons ncdr) { item=ncar; rest=ncdr; };
public Object car()
{ return item; };
public Cons cdr()
{ return rest; };
public void setCar(Object o) throws ClassCastException
{ item = o; };
public void setCdr(MutableCons m) throws ClassCastException
{ rest=m; };
};
Implement lambda expressions in Java. A lambda expression should conform to the following interface:
public interface Lambda {
Lambda lambda(Lambda l);
Lambda apply(Lambda l);
Lambda beta(Lambda l);
Lambda eta();
}
Beta reduction should implicitly perform alpha reduction if required in order to maintain consistent bindings. The class should also implement the method toString from java.lang.Object.
public abstract class LambdaExpression implements Lambda { ..... };
public class Name extends LambdaExpression { .... };
public class Abstraction extends LambdaExpression { .... };
public class Application extends LambdaExpression { .... };
public class Parentheses extends LambdaExpression { .... };
If you want to get fancy, you add Number, PrimOp.
class Name;
public abstract class LambdaExpression implements Lambda {
protected abstract boolean isBound(Name n);
protected abstract LambdaExpression replace(Name n, LambdaExpression);
};
public class Name extends LambdaExpression {
protected String name;
private static int counter = 0;
public Name() { s = "AName" + (String) counter; counter++; }
public Name(String s) { name=s; };
public Lambda lambda(Lambda l) {
return new LambdaAbstraction(this, l);
};
public Lambda apply(Lambda l) {
return new Application(this, l);
};
public Lambda beta(Lambda l) {
return l;
};
public Lambda eta() {
return this;
};
protected boolean isBound(Name n) {
return (n.name==name);
};
protected LambdaExpression replace(Name n, LambdaExpression l) {
if (n.name==name) {
return l;
} else {
return this;
}
};
};
public class Abstraction extends LambdaExpression {
protected Name name;
protected LambdaExpression expr;
public Lambda lambda(Lambda l) {
return new Abstraction(new Name(), Application(this, l));
};
public Lambda apply(Lambda l) {
return new Application(this, l);
};
public Lambda beta(Lambda l) {
if (expr.isBound(name))
return expr.replace(name, l);
else return this;
};
public Lambda eta() {
if (expr.isBound(name))
return this;
else return expr;
};
protected boolean isBound(Name n) {
return expr.isBound(n);
};
protected LambdaExpression replace(Name n, LambdaExpression l) {
return expr.replace(n, l);
};
public class Abstraction extends LambdaExpression {
protected LambdaExpression f;
protected LambdaExpression n;
public Lambda lambda(Lambda l) {
return new Abstraction(new Name(), Application(this, l));
};
public Lambda apply(Lambda l) {
return new Application(this, l);
};
public Lambda beta(Lambda l) {
return this; // beta only does something with abstraction
};
public Lambda eta() {
return this;
};
protected boolean isBound(Name name) {
return f.isBound(name) || n.isBound(name);
};
protected LambdaExpression replace(Name name, LambdaExpression l) {
return new Application(f.replace(name, l), n.replace(name, l));
};
Has a name. This is also called the functor.
Has some number of arguments, each with a variable (a name)
The name and the arguments are the head or the consequent.
Has zero or more terms.
Each term identifies another clause.
Unification is commutative.