Friday, November 27, 2009

Extension methods: the case for language-level delegates in Java

Extension methods

In There's not a moment to lose!, Mark Reinhold announces something completely different: Java 7 will have closures after all. Huzzah! And in that announcement, he lists enabling language changes for them:

  • Literal syntax
  • Function types
  • Closure conversion
  • Extension methods

I want to look at extension methods closely. Alex Miller does an admirable task of summarizing JDK7 changes; that's a good place to start. And some code:

public final class Filtering {
    public static <T, C extends Collection<T>> C filterInto(
            Collection<T> input, C output, Filter<T> filter) {
        for (T item : input)
            if (filter.keep(item))
                output.add(item);

        return output;
    }

    interface Filter<T> {
        boolean keep(T element);
    }
}

Given extension methods, a coder can write:

// static imports
Collection<String> names = asList("Bob", "Nancy", "Ted");
List<String> bobNames = names.filterInto(
        new ArrayList<>(), // new diamond syntax
        #(String name) "Bob".equals(name)); // new closure syntax

And some magic means will find Filtering#filterInto(Collection, Collection, Filtering.Filter) and transform the instance method call on Collection into a static method call on Filtering. Rules for finding Filterable vary. (Did you read Alex Miller's link?)

Here is an alternative.

Delegation

I am a fan of delegates and have writen about them often: I'd like them in Java. With delegates, the magic for finding #filterInto is less magical, and more under the control of the coder, but just as clean to use:

public final class FilteringCollection<T>
        implements Collection<T>, Filtering {
    // Some literate programming sugar for the static importers
    public static <T> FilteringCollection<T> filtering(
            Collection<T> input) {
        return new FilteringCollection(input);
    }

    public FilteringCollection(final Collection<T> input) {
        this = input; // the delegation proposal
    }

    public <C extends Collection<T>> C filterInto(
            C output, Filter<T> filter) {
        for (T item : this)
            if (filter.keep(item))
                output.add(item);

        return output;
    }
}

Usage now looks like:

// static imports
FilteringCollection<String> names = filtering(asList("Bob", "Nancy", "Ted"));
List<String> bobNames = names.filterInto(
        new ArrayList<>(), // new diamond syntax
        #(String name) "Bob".equals(name)); // new closure syntax

What's the difference?

Key here is that for the coder the only difference is delegation uses a static factory method to wrap input. This is not a drawback: it is an advantage. Less magic makes Java programs easier to maintain. There is no loss here in power.

Why bother with the delegation proposal?

Would you like to write the boilerplate forwarding methods of Collection? I wouldn't! It gets even worse as you move down the inheritance tree for Collection.

Postscript

How would you move down Collection's inheritance tree any how?

Beyond any current proposals, some javac author could implement type erasure for base classes (very carefully) in context of delegation:

public final class FilteringCollection<T, D extends Collection<T>>
        implements D, Filtering {
    // Only factory/constructor changes
    public static <T, D extends Collection<T>> FilteringCollection<T>
            filtering(D input) {
        return new FilteringCollection(input);
    }

    public FilteringCollection(D input) {
        this = input;
    }
}

Because the coder need not implement the dozens of forwarding methods, neither does he need to know what they are! The compiler sets the type of D to the precise collection used in the factory/constructor, and the resulting object provides all public methods.

But this is well beyond the scope of delegation. Go code Scala, or something.

UPDATE: Food for thought on extension methods from RĂ©mi Forax.

UPDATE #2: This just gets more interesting: in Extension methods vs. method currying, Stefan Schulz discusses the alternative of currying.

Wednesday, November 18, 2009

A future for Java after all?

One of my favorite bloggers, Alex Miller, opens today with Apparently Mark Reinhold announced that closures would be added to JDK 7 at Devoxx today.

When I realized JDK7 would have no closures, I mentally wrote Java off as a language destined to be COBOL or FORTRAN or C: important in a practical way, but not to be where my heart was. (I'm looking around; Scala is likely my next language of choice.)

And, lo! Closure—in Java! Java may remain after all the language I prefer most. What a surprising turn.

UPDATE: Stephen Colebourne posts more details.

Thursday, November 12, 2009

Frankie says, don't do it (but does anyway)

Aleksander Kmetec explains Acme::Dont implemented in Scala.

Is it scarier that I can follow along, or that I recognize the Perl don't pragma? (Naturally Damian Conway is to blame.) Funny I ran across this post while reading Bytecodes meet Combinator: invokedynamic on the JVM.

Wednesday, November 11, 2009

How are your algorithms?

A fabulous post by Peteris Krumins summarizing MIT's Introduction to Algorithms lectures. It's like having a little Knuth in your browser.

A new programming language: Go

Time to brush up my resume and add a new programming language: Go, say 3 years experience?

I chuckle as I type this, but I have interviewed candidates for my present employer with an equally tenuous grasp on Java at variance with their resumes.