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);
}
}