Wednesday, 23 August 2006

Extension methods and closures

Extension methods are a C# feature that just could prove very useful in Java, particularly with the possibility of closures in Java (something which should be the topic of a blog in its own right). So, what is an extension method, and why might it be useful? Consider the following code:

public class Collections {
 pubilc static void sort(List list, Comparator comp) {
   ...
 }
}
public MyClass {
 pubilc void myMethod() {
  List list = ...;
  Comparator comp = ...;
  Collections.sort(list, comp);
 }
}

This is standard JDK code. To sort a list you have to use the Collections static methods. But that means you need to know that they exist there - they won't auto complete like other list methods in an IDE for example. What if we could find a way to alter this slightly.

public class Collections {
 @extensionMethod(Sort)
 pubilc static void sort(List list, Comparator comp) {
   ...
 }
}
public MyClass {
 pubilc void myMethod() {
  List list = ...;
  Comparator comp = ...;
  list.Sort(comp);
 }
}

So, by adding an attribute (or some other syntax mechanism - C# uses 'this' rather strangely) and importing the Collections class, we can then call the sort method in an OO style in the client code myMethod(). I have deliberately setup a coding standard, so that the extension method has a capital first letter. This would allow someone reading the code to spot that it is not a standard method on the class. Behind the scenes, all it does is call the static method in a static way - no magic involved.

This brings benefits in being able to effectively extend code that is locked down in some way - perhaps because its an interface (such as the JDK List interface), or perhaps because its an 3rd party library that you don't want to change, but is really missing a very useful little method.

So, how does this link to closures? Well, many closure advocates want to add methods to Collection, List and Map to allow closures to operate directly on the collection. But this simply isn't going to happen as they are interfaces and backwards compatability must be maintained. However, extension methods offer a solution - a way to apparantly add methods to the collection interfaces. Thus you could code with closures:

public class Collections {
 @extensionMethod(Each)
 pubilc static void each(Collection<T> list, void(T) closure) {
  for (T item : list) {
   closure(item);
  }
 }
}
public MyClass {
 pubilc void myMethod() {
  List<String> list = ...;
  list.Each() (String str) {
   System.out.println(str);
  };
 }
}

So, are there any views about extension methods? Are they really an essential addon to the closure proposal?

3 comments:

  1. Nice (http://nice.sourceforge.net) ha(s|d) this, sort of. It is called Multimethod there and allows the definition without a static class. I think Nice got killed by Scala.

    Your suggestion _is_ nice, as it goes well with standard Java syntax and allows leveraging existing code that works around missing comprehension.

    I am not really sure to used the annotation, Gilad will argue that is an abuse

    ReplyDelete
  2. Multimethods aren't extension methods, if I understand them. I thought multimethods were about dynamic dispatch based on parameter type (not just the defining class).

    My recommendation for extension methods, if such a thing is to be done, is to turn the tables on mixins. Rather than say "mix this into my class, please", you just define stuff that gets automatically mixed in if imported by the caller. So, for instance:

    module Collections extends List {
    public void each(void(T) closure) {
    // ...
    }
    }

    It would effectively become a static method. Anyway, or something like that.

    Although I still don't like the semantics-free "void(T)" stuff. I'd prefer single-method interfaces only.

    ReplyDelete
  3. Yeah, its not multimethods, its just that nice has package level methods and makes no distincition between object.doStuff(a,b,c) and doStuff(object,a,b,c). Something like reverse mixins (using whatever syntax) would be a far better way to handle Collections and other utility classes.

    ReplyDelete