The Open/Closed Dilemma

08 Aug 2014

Let's start with just a smidgeon of code I've been mulling over recently:

def a
  'a'
end

def b
  a
end

a() #=> "a"
b() #=> "a"

def a
  'b'
end

a() #=> "b"
b() #=> "b"

When you think about this example, it really shows the beauty of working in a dynamic language. This is the kind of code that allows mocking to come much more easily when everything is message based and dynamic. As a proud proponent of static languages, I can't help but find this to be attractive and yet dangerous. Like uncle Ben says: "With great power comes great responsibility."

In most static languages, that example is absolute rubbish. The compiler would be SO upset with you for redefining a method because most static languages do not perform lookups by name at runtime, so the only way to link properly is to only allow one method definition with a given signature, right? I've been thinking about that assumption, and I'm really beginning to think it's not true. I'm thinking that something like this might be possible and even useful in a static language by exploiting lexical scope. We all know how to read lexically scoped code in static languages, but they let us redefine symbols in different contexts to have different meanings, so what would this look like in say, Java?

public class Person implements Comparable<Person> {
    String name;
    int age;

    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public int compareTo(Person other) {
        return name.compareTo(other.name);
    }
}

public class PeopleByAge {
    @Redefine(Person.class)
    private class Person implements Comparable<Person> {
        @Override
        public int compareTo(Person other) {
            return Integer.compare(age, other.age);
        }
    }

    SortedSet<Person> people = new TreeSet<Person>();
}

The idea here is that in the context of PeopleByAge, the Person class compares using the age property, while in the rest of the world they are compared by the name property. This is useful enough, but the astute reader will know that the TreeSet in particular can use a Comparator as one of its constructor parameters to do this very thing. The advantage of having this dynamism is that not all library authors will allow such a re-definition.

The really important part of selling this to static languages, I believe is the distinction about what methods can be overridden and where. While Java uses virtual dispatch for most (all?) of its method calls, one would not expect someone to be able to override Person.getAge(). I tend to think that this could be a great mechanism for re-implementing existing interfaces or even potentially trying something like C# has to extend classes with new methods. I would even take it one step further and say that it might be nice to be able to add an interface to a class in such a lexically scoped way.

Ultimately this is not a serious proposal because it would require completely overhauling the JVM, but it remains an interesting idea that I would love to explore.