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.

8 comments:

Bob said...

Mark, you are the man!

What strikes me is that the proliferation of interfaces is just the tip of an exponential iceberg. BGGA may hide these explicit interfaces, but it doesn't address the exponentially larger combination explosion in code which uses them. I certainly don't want to write and maintain the same code over and over for N combinations just to support primitives.

The Java community should take a step back and solve the much more fundamental problem of how primitives and generics interact before we tackle closures of any kind.

In regard to the CICE prototype, I know this will be tricky, but the method parameter types should be explicit, and we should infer the generic type arguments. That will be consistent with what we already see with generic type inference in methods and with what we hope to see for constructors in the future. The same inference work could apply to anonymous inner classes, too.

Thanks again for this awesome contribution.

Mark Mahieu said...

Hi Bob,

Yeah the whole generics vs primitives things is painful. It does seem that BGGA could incorporate auto-boxing/unboxing, but I'm not sure it would be a good idea.

Also, I absolutely agree that to infer the type arguments of a CICE would be the better option. I'm exploring a couple of approaches at the moment, and it's certainly an interesting problem :)

Thanks for your comments!

Mark

Stephen Colebourne said...

I don't know the practicality of this, but maybe we should consider allowing primitives as generic types, eg. Pair[int, String] ?

Bob said...

Stephen, I'm starting to think that's one of many things we should clean up before adding closures. Adding closures now would amplify existing quirks and holes like this. FWIW, C# supports primitives as generic types by generating primitive-versions of code at runtime. It's a little bloated but way better than writing N versions of the same code by hand.

Unknown said...
This comment has been removed by the author.
Unknown said...

Hmm, Ive been trying to work with the new prototype at http://slm888.com/javac/index.html and somehow I cant get the compiler working to test some of the code written according to the CICE proposal.
I keep getting the error:
javac: invalid source release: cicearm

I have the cicearm-b05 version. Can you help me figure out whats wrong?

Mark Mahieu said...

vikas: It looks like there's a bug in the javac.bat script - are you running it on Windows?

I've just uploaded a fixed version at

http://slm888.com/javac/files/javac.bat

which you should be able to drop into the bin folder, overwriting the old version.

Unknown said...

Yes Mark! Thanks a lot, it works fine now :-). And yeah, I am on windows for now :(, not for long I hope!