Sunday, February 17, 2008

Wednesday, February 6, 2008

Say it again SAM

CICE is designed to make it easier to create instances of SAM types - interfaces and abstract classes which have a Single Abstract Method. The proposal itself also contains the following statement:

At the same time as this facility is added to the language, it would make sense to add a few more single method interface types, such as Predicate, Function, and Builder.

It's no coincidence that the jsr166y.forkjoin framework from Doug Lea and the rest of the jsr166 group is defining a number of interfaces like these for that framework's own needs - in fact the commonality is explicitly acknowledged in the documentation:

Preliminary release note: Some of the declarations in this class are likely to be moved elsewhere in the JDK libraries upon actual release, and most likely will not all nested in the same class.

I've just uploaded a new build of the CICE/ARM prototype, which includes a number of interfaces derived from those currently defined by jsr166y.

The differences between the interfaces in the prototype, and those provided by jsr166y are:

  • I've put them in a new package - java.util.ops

  • It includes versions for all primitive types

  • All Op types come in two forms - one which can throw checked exceptions, and one which cannot

  • Op types no longer have anything useful to say in their javadoc

All of these changes are possible consequences of removing the interfaces from the context of a specific framework, and in particular, a framework for which only a limited number of these interfaces are useful.

Unfortunately it adds up to a massive increase in the number of interfaces defined, from 132 in jsr166y.forkjoin.Ops, to 1828 in java.util.ops, and it's possible to define even more variations. Ouch :( Kingdom of the Nouns indeed...

Still, it's a straw-man, and no doubt it can be improved. Some thoughts I've had on this are:

  • Placing them all in java.util.ops means Comparator is defined in two different packages, so it's not ideal.

  • It could be argued that providing versions for all primitive types is overkill. However, without knowing the requirements of future libraries which may make use of them, it's difficult to know which ones can be omitted. Type promotion helps for the parameter types, but it's no use with return types.

  • Is it useful providing checked exception versions of all the Op types? Would it be more appropriate to do this for Procedure and friends?

Stephen Colebourne recently compared writing the same snippet of code in BGGA, FCM and CICE, and noted that a CICE can be rather more verbose due to the repetition of type names. His example used an interface called PairMatcher, defined as follows:

public interface PairMatcher<T,U> {
    boolean matches(T arg1, U arg2);

Creating an instance of this using earlier versions of the prototype would look something like this:

PairMatcher<String,Integer> pm = PairMatcher<String,Integer>(String str, Integer val) {
    return (str != null && str.length() > 0 && val != null && val > 10);

However, it has been pointed out that the parameter types are not really necessary, since there is only one method which a CICE can legally implement. So the latest version of the prototype allows you to do just that:

PairMatcher<String,Integer> pm = PairMatcher<String,Integer>(str, val) {
    return (str != null && str.length() > 0 && val != null && val > 10);

I'm not a big fan of that syntax personally - I'd rather lose the type arguments - but it's an option.

Thoughts very welcome, as usual.