// Using phantom types in GC# // This program requires .Net version 2.0 // Peter Sestoft (sestoft@itu.dk) * 2001-10-26 v 0.2 // A typed (int, bool) embedding of object expressions as in // Leijen and Meijer 1999 section 5.1. // The `phantom' types parameters are actually used in the types of // methods in the objects representing the expressions; not `phantom'. // Representing object language *variables* in a typed manner seems // much harder, unless variables of different types are distinguished, // and we have distinct environments for every type of variable when // evaluating the expression. using System; abstract class E { // R = result type abstract public R eval(); } class Lit : E { private readonly R v; public Lit(R v) { this.v = v; } public override R eval() { return v; } } abstract class Bin : E { // S = subexpr type; R = result type protected readonly E e1, e2; public Bin(E e1, E e2) { this.e1 = e1; this.e2 = e2; } } class Add : Bin { public Add(E e1, E e2) : base(e1, e2) { } public override int eval() { return e1.eval() + e2.eval(); } } class Eq : Bin { // S = subexpr type public Eq(E e1, E e2) : base(e1, e2) { } public override bool eval() { return e1.eval().Equals(e2.eval()); } } class And : Bin { public And(E e1, E e2) : base(e1, e2) { } public override bool eval() { return e1.eval() && e2.eval(); } } // Exercising the encoding class Phantom { // Build the expression n + ... + 1 + 0 static E sum(int n) { if (n == 0) return new Lit(0); else return new Add(new Lit(n), sum(n-1)); } static void Main() { E e1 = sum(40); E e1Eq55 = new Eq(new Lit(55), e1); E e1Ne55 = new Eq(new Lit(false), e1Eq55); Console.WriteLine(e1.eval() + " " + e1Eq55.eval() + " " + e1Ne55.eval()); E e2 = new Lit("foo"); // typesafe, no confusion possible } }