Wednesday, December 1, 2010

Defender Methods: Neal Gafter is *not* painting a bike shed

I follow the lambda-dev mailing list, and there is a discussion going on regarding defender methods and automatic disambiguation. Defender methods are a language feature that allows some interesting things:
  1. Adding methods to existing interfaces without breaking backwards compatability
  2. Multiple inheritance of behavior (but not state-- think interfaces with code)
The second item is profound (well both are), and it will be interesting to see how its usage is adopted by the community. The approach taken with defender methods is described as "declaration-site" extension methods, because it has a syntax present in the interface declaration. For example, imagine adding a "randomElement" method to java.util.Collection. Today, you can't do that without breaking every class that implements Collection. Using a defender method you can do:

interface Collection<T> {
T randomElement() default Collections.randomElement;
...
}

// add the default implementation to Collections
public class Collections {
...
public static <T> randomElement(Collection<T> c) {
...
}
}
Other languages implement use-site extension methods (e.g. C#) in which case the owner of Collections doesn't have change anything. You could write (in C#)

namespace SteveCode.Extensions {
static class MyCollections {
T randomElement(this ICollection<T> c) {
...
}
}
}
Then anywhere you state "using SteveCode.Extensions" (i.e. "import" the namespace) then you can invoke someCollection.randomElement() and the compiler will invoke the static method. Ergo, the "user" of the extension method has to indicate that he is using it.

There are some great articles out there discussing the pros/cons of declaration vs use site approaches, so I wont re-hash them. See Remi Forax's post and another.

Java is going with declaration-site style and one of the benefits as now inheritance can still work. I.e. if you augment an interface to add a new method with a "default" and then later in your concrete class you wish to specialize this new method, you can override it and consumers will call the specialized method via polymorphism (as expected). This doesn't work with use-site extension methods as they are just a compiler trick.

So on the lambda-dev list, there has been a discussion over a particular feature in the current specification. Take the following:

public class Impls {
public static void one(Object o) { ... }
public static void two(A a) { ... }
public static void two(B b) { ... }
}

interface A {
void execute() default Impls.one;
}
interface B {
void execute() default Impls.one;
}
If you defined a class MyAB implements A,B what would you expect to happen? Well currently the spec allows this "automatic disambiguation" because they both delegate to the same method/overload. Where this becomes a problem is if later the owner of B changes the default of B to something else. Now when you recompile MyAB suddenly it breaks and you must manually disambiguate by implementing execute inside MyAB.

Note that if the interfaces took a different overload of the default, for example:

interface A {
void execute() default Impls.two;
}
interface B {
void execute() default Impls.two;
}
Now class MyAB implements A,B would fail right off the bat the first time-- because there is no overload in common. Thus, automatic disambiguation would not work in this case either (nor would you want it). Lastly, imagine having:

public class Impls {
public static void one(Object o) { ... }
public static void two(Object o) { ... }
public static void two(A a) { ... }
public static void two(B b) { ... }
}
In this case, I think (and someone please correct me if I'm wrong) that normal overload rules will apply and this will compile, invoking two(Object o) in MyAB, but in another class that only extends A, it would call two(A a).

There is also some additional complexity that must be added somewhere (talk of changing the .class file format to include linking information) so that in the presence of changing versions of the interface, the code compiled against the old version will still work (remain binary compatible).

All in all, there is some complexity for doing this "automatic disambiguation". Neal Gafter of Java Puzzlers, Sun Java spec, etc. fame (not in that order) has raised the point of whether this complexity is worth it. One of his proposed solutions is just to use method bodies in the interface itself. This would remove the overload resolution of the "default" entirely, moving the disambiguation to the client at the very beginning instead of making it potentially a source incompatible change to modify the defender's default (probably unexpected). This seems like an entirely reasonable conversation to have on the mailing list as its a language detail that has the potential to have expensive impacts. The discussion was going on for a few days-- and Reinier Zwitserloot (of Project Lambok, etc. fame) chimes in with:

When is the currently proposed "default method is part of the signature" aspect going to result in a problem? If this is about ideological purity, then I vote we stop painting this bikeshed.

If you don't get the bikeshed reference, check here. In classic Neal fashion, he had a precise (ableit somewhat arrogant) retort, which upon reading I had a good chuckle:

An interesting reference to Parkinson's *law of triviality*. The basis of Parkinson's law is that people seem to care less about issues they don't understand, no matter how important those issues might be relative to the issues for which they express a preference. "painting this bikeshed" is an *ad hominem* suggestion that the participants in this discussion are really only commenting on those issues simple enough for them to understand.

In this case, however, you also appear to be saying that you don't fully appreciate the issue, and therefore don't see any point in continuing the discussion. Which ironically reinforces the reference to Parkinson's law.

I like Neal Gafter and Reinier Zwitserloot and am happy that everyone on the dev lists cares enough to take time to discuss these things for the (I believe) betterment of the Java community. Its not to say there should be unbounded discussion, but I personally found Neal's points useful. Automatic disambiguation does seem to require complexity that outweighs its benefit-- and that's worth discussing.

Steve