Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save contrun/576c65b7889b3bc81db97284e4edc7c1 to your computer and use it in GitHub Desktop.

Select an option

Save contrun/576c65b7889b3bc81db97284e4edc7c1 to your computer and use it in GitHub Desktop.

Revisions

  1. contrun renamed this gist Mar 27, 2024. 1 changed file with 0 additions and 0 deletions.
  2. contrun renamed this gist Mar 27, 2024. 1 changed file with 2 additions and 2 deletions.
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    // Wadler's the expression problme email https://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt
    // Wadler's the expression problem email https://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt
    // introduced the term expression problem and suggested a solution in Generic Java. The code within the email
    // actually does not work. Below is Wadler's original words.

    @@ -27,7 +27,7 @@
    // I tried to use `This.Visitor` in interface `PlaneShape`, which failed with error
    // Cannot make a static reference to the non-static type This Java (536871434)
    // This seemed to be essentially Wadler's caveat.
    // I don't know if we can work around this 30 years later.
    // I don't know if we can work around this 20 years later.

    // class Module1F<This extends Module1F<This>> with
    // final class Module1 extends Module1F<Module1> {}
  3. contrun created this gist Mar 27, 2024.
    145 changes: 145 additions & 0 deletions wadler-expression-problem-generic-java-code-in-moder-java.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,145 @@
    // Wadler's the expression problme email https://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt
    // introduced the term expression problem and suggested a solution in Generic Java. The code within the email
    // actually does not work. Below is Wadler's original words.

    // 2. A caveat with regard to inner interfaces

    // In GJ as it is currently implemented, type parameters do not scope
    // over static members, and further, a type parameter may be indexed only
    // by non-static classes or interfaces defined in the bound. And in Java
    // as it is currently defined, all inner interfaces are taken as static,
    // whether declared so are not. This makes the mechanism for indexing
    // type variables by inner classes useless for interfaces, greatly
    // reducing its utility. In particular, it invalidates the solution just
    // presented, which depends on Visitor being an interface.

    // Fortunately, it looks possible to relax either the GJ or the Java
    // constraint. So far as I can see, the only difference in making an
    // interface non-static is that it can now include non-static inner
    // classes; so the change would not render invalid any existing Java
    // programs. But this point requires further study. Also, I should note
    // that since the changes have not been implemented yet, I have not
    // actually run the proposed solution. (I did translate from GJ to Java
    // by hand, and run that.)

    // I tried to port the original code into modern java (with a different example).
    // But I found I was unable to refer to the sibling trait within the same class.
    // I tried to use `This.Visitor` in interface `PlaneShape`, which failed with error
    // Cannot make a static reference to the non-static type This Java (536871434)
    // This seemed to be essentially Wadler's caveat.
    // I don't know if we can work around this 30 years later.

    // class Module1F<This extends Module1F<This>> with
    // final class Module1 extends Module1F<Module1> {}
    // can be used to make This type variable in Module1F refer to exactly the
    // same class (instead of possibly subclasses).
    // Quoting The Expression Problem by Philip Wadler
    // This use of `This' is the standard trick to provide accurate static typing in the prescence of subtypes (sometimes called MyType or ThisType).
    // See also Is there a way to refer to the current type with a type variable?
    // https://stackoverflow.com/questions/7354740/is-there-a-way-to-refeclass
    class Module1F<This extends Module1F<This>> {
    // A Visitor trait that is bounded by the trait Module1F.
    // We may think this as a Visitor specialized to the PlaneShape defined below.
    // Quoting The Expression Problem by Philip Wadler
    // The key trick here is the use of This.Exp and This.Visitor, via the
    // mechanism described in `Do parametric types beat virtual types?'.
    // Recall that mechanism allows a type variable to be indexed by any
    // inner class defined in the variable's bound.
    interface Visitor<R> {
    public R forTriangle(Double a, Double b, Double c);
    }

    // The data type we want to extend.
    interface PlaneShape {
    // Instead of passing a generic Visitor to this function, we pass a
    // This.Visitor.
    // This sibling interface Visitor may use methods specific to some data
    // variants,
    // e.g. forTriangle method specific to Triangles here!

    // There is actually an error in the code below. The error is:
    // Cannot make a static reference to the non-static type This Java (536871434),
    // which as far as I know means that This.Visitor<R> is not a static type,
    // and accessing This.Visitor<R> from a static context is not allowed.
    public <R> R visit(This.Visitor<R> v);
    }

    // A data type variant of the PlaneShape interface.
    class Triangle implements PlaneShape {
    protected final Double a_, b_, c_;

    public Triangle(Double a, Double b, Double c) {
    a_ = a;
    b_ = b;
    c_ = c;
    }

    // The public entry point for the visitor, used to run a specific operator for
    // this data variant.
    public <R> R visit(Visitor<R> v) {
    return v.forTriangle(a_, b_, c_);
    }
    }

    // Implement an operator based on visitor pattern.
    class Perimeter implements Visitor<Double> {
    public Double forTriangle(Double a, Double b, Double c) {
    return a + b + c;
    }
    }
    }

    final class Module1 extends Module1F<Module1> {
    }

    class Module2F<This extends Module2F<This>> extends Module1F<This> {
    interface Visitor<R> extends Module1F.Visitor<R> {
    public R forSquare(Double x);
    }

    class Square implements PlaneShape {
    protected final Double x_;

    public Square(Double x) {
    x_ = x;
    }

    public <R> R visit(Visitor<R> v) {
    return v.forSquare(x_);
    }
    }

    class Perimeter extends Module1F<This>.Perimeter implements Visitor<Double> {
    public Double forSquare(Double x) {
    return 4 * x;
    }
    }

    class Area implements Visitor<Double> {
    public Double forTriangle(Double a, Double b, Double c) {
    Double s = (a + b + c) / 2.0;
    Double area = Math.sqrt(s * (s - a) * (s - b) * (s - c));
    return area;
    }

    public Double forSquare(Double x) {
    return x * x;
    }
    }
    }

    final class Module2 extends Module2F<Module2> {
    }

    final class Main {
    static public void main(String[] args) {
    Module2 m1 = new Module2();
    Module2.PlaneShape e1 = m1.new Triangle(3.0, 4.0, 5.0);
    System.out.println("Perimeter: " + e1.visit(m2.new Perimeter()));

    Module2 m2 = new Module2();
    Module2.PlaneShape e2 = m2.new Square(3.0);
    System.out.println("Perimeter: " + e2.visit(m2.new Perimeter()));
    System.out.println("Area: " + e2.visit(m2.new Area()));
    }
    }