Wednesday, February 13, 2008

An alternate syntax for Java closures

I'm no expert on the details of the closures proposals. I do think if we're going to put closures into the language, we need to make them complete, full-fledged closures. Generics are a mess precisely because the semantics and features were compromised for the goal of getting it into the language.

But one thing I do know about the BGGA closures proposal is that I don't like the syntax. Take the example in Weiqi Gao's recent Java Quiz:

public class Fib {
private static {int=>int} fib = { int n =>
n==0 ? 0 :
n==1 ? 1 :
fib.invoke(n-1) + fib.invoke(n-2)
};

public static void main(String[] args) {
System.out.println(fib.invoke(6));
}
}

To me, this is just hard to read. The whole "=>" thing seems like a weak attempt to create a new lexical character and is too visually similar to ==, >>, etc. I also don't like the 'invoke' syntax. I was discussing this with Weiqi the other day and came up with a syntax that I like better.

public class Fib {
private static {int # int} fib = { # int n #
n==0 ? 0 :
n==1 ? 1 :
#fib(n-1) + #fib(n-2)
};

public static void main(String[] args) {
System.out.println(#fib(6));
}
}

Note that you could also invoke fib like this:

System.out.println(#Fib.fib(6));

I wonder what other people think of this alternative?

8 comments:

Michael Easter said...

re: =>. My guess is that this is inspired by Scala. See the PDF of Tim Dalton's slides (look for functions):

http://tinyurl.com/yunx8t

Odersky was involved in the early Generics prototype and I wonder if that is the link. (This is pure speculation on my part. I don't know if he even espouses closures.)

I was originally put off by => but now like it. I prefer it over #. Using -> versus => is an interesting debate.

Because fib is a function, I think invoke is quite readable. There is enough subtlety with closures! ;-)

Alex Miller said...

The FCM proposal shares some overlap with your ideas; not sure if you've seen it. I like FCM's syntax except for their placement of the return type. I understand their reasoning, just don't like it.

djo.mos said...

Well, to be honest, the BGGA seems more readable and less verbose than the # notation you propose ... especially for the # before int n, which seems ... out of phase.
Isn't this similar to the Ruby notation once you replace the # by the | ?

Cheers.

Brian Gilstrap said...

Yes, the syntax is similar to Ruby's. I don't like Ruby's vertical bars much (probably due to my background with Unix shells and pipes). But I like the concise syntax of Ruby's closures quite a bit. I find it easier to locate the arguments to the block when they are 'bracketed' by characters.

I don't like 'invoke' because it's so long. I suppose an alternative would be to treat closures as if they were functions and just say fib(12). I don't know what this would mean for parser complexity. I really, really don't like 'invoke'.

I just took a quick look at the FCM proposal. I may be misunderstanding it but it seems that it's semantics/mechanisms are very different than BGGA. At first blush I don't care much for the direction they seem to be headed. I'm essentially proposing an alternate syntax for BGGA.

Howard Lovatt said...

In C3S:

http://www.artima.com/weblogs/viewpost.jsp?thread=182412

The example is:

private static Method[Integer,Integer] fib = method(int n) {
__n==0 ? 0 :
__n==1 ? 1 :
__fib.call(n-1) + fib.call(n-2)
};

Brian Gilstrap said...

Based upon the URL provided for C3S, I was unable to find any syntax that looks like that specified. Can you point me at an explanation of what this syntax means?

Howard Lovatt said...

@Brian,

In my example I swapped angle brackets for [] and used _ to indent because of posting convenience. Method1[Integer,Integer] is a generic interface that has a method call. The keyword method introduces anonymous method definitions.

Owen Fellows said...

I am no expert either but it seems like you want a strongly typed instance of a Method object, for that to be possible you need to define the return type and passed arguments.

public class Example
{

public static final void args(String[] args)
{
Example example = new Example();

List numbers = new ArrayList<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);

// Calling a method with a closure
Boolean[Integer i] numberEqualsThree = new Boolean[Integer i]{ return i == 3; };
example.methodThatTakesAClosure(numbers, numberEqualsThree);

// Calling a closure which does recursion
final Integer[Integer i] fib = new Integer[Integer i]{ return n==0 ? 0 : n==1 ? 1 : fib(n-1) + fib(n-2)};
Integer result = fib(8);

// Calling a closure with recursion that knows about itself
Integer result = new Integer[Integer i]{ return n==0 ? 0 : n==1 ? 1 : this(n-1) + this(n-2)}(8);
}

public Integer methodThatTakesAClosure(List<Integer>>integers, Boolean[Integer i] aClosure)
{
for (Integer integer : integers)
{
if (aClosure(integer))
{
return integer;
}
}
}

}