public class SessionTypes { public static final class Eps { } public static final class Send { } public static final class Recv { } public static final class Choose { } public static final class Offer { } public static final class Dual { private Dual() { } public static Dual eps = new Dual(); public Dual, Recv> send() { return new Dual, Recv>(); } public Dual, Send> recv() { return new Dual, Send>(); } public Dual, Offer> choose(Dual right) { right.nullCheck(); return new Dual, Offer>(); } public Dual, Choose> offer(Dual right) { right.nullCheck(); return new Dual, Choose>(); } public Dual flip() { return new Dual(); } public void nullCheck() { } } public static final class Unit { } public interface Func { B call(A a); } public static final class Session { private static interface Thunk { A force(); } private final Thunk action; private Session(Thunk thunk) { action = thunk; } public Session(final A a) { action = new Thunk() { public A force() { return a; } }; } public Session bind(final Func> func) { return new Session(new Thunk() { public B force() { return func.call(action.force()).action.force(); } }); } public Session bind(final Session next) { return new Session(new Thunk() { public B force() { action.force(); return next.action.force(); } }); } public static Session, S, Unit> send(final A a) { return new Session, S, Unit>(new Thunk() { public Unit force() { return new Unit(); } }); } public static Session, S, A> recv() { return new Session, S, A>(new Thunk() { public A force() { throw new Error(); } }); } public static Session, S, A> mock_recv(final A a) { return new Session, S, A>(new Thunk() { public A force() { return a; } }); } public static Session, R, Unit> sel1() { return new Session, R, Unit>(new Thunk() { public Unit force() { return new Unit(); } }); } public static Session, S, Unit> sel2() { return new Session, S, Unit>(new Thunk() { public Unit force() { return new Unit(); } }); } public static Session, U, A> offer(final Session left, final Session right) { return new Session, U, A>(new Thunk() { public A force() { return left.action.force(); } }); } public static Session close = new Session(new Thunk() { public Unit force() { return new Unit(); } }); public static A run(Dual witness, Session r, Session s) { witness.nullCheck(); return s.action.force(); } } public static void main(String[] args) { Session, Unit, Unit> a = Session .send("meh!") .bind(Session.close); Session, Unit, String> b = Session .mock_recv("hello") .bind (new Func>() { public Session call(String s) { return Session .close .bind(new Session(s)); } }); String result = Session.run(Dual.eps.send(), a, b); System.out.println(result); // How about a null witness? Should raise a // NullPointerException instead of running the session: Session.run(null, a, b); } }