Atom Atom feed

Dreaming Up Syntax For Function Types For BGGA Closures

Why should Stephen Colebourne have all the fun?

Stephen Colebourne: Function types, or method types as FCM refers to them, are one of the most controversial features of closures. Is there an alternative that provides 80% of the power but in the style of Java?

Stephen went on to derive an alternative syntax for the part of BGGA closures proposal that's hardest for ordinary Java programmers to "get".

To understand what Stephen's talking about, look again at my Friday Java Quiz closure example 20 days ago:

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));
  }
}

Knowing that most ordinary (by which I mean non-new-language-feature-designing) Java programmers are not following the closures development, I put this example on the white board in my office and quizzed everyone walking by. Of the six or seven extremely bright people who were put on the spot by my question "Come here. Take a look at this and see if you can figure out what this program means" only Rob deciphered the program without any hint.

There are three difficulties associated with this piece of code:

  • The function type syntax (the red part)
  • The input variables specification syntax (the blue part)
  • The invocation syntax (the green part)

Of the three, the function type syntax is the most severe cognitive barrier. Most of my innocent victims simply get stuck at the notation and can't parse any further. I have to draw a black box around it and say "This is a type" before they can make sense of the rest of the example. They'll then say "Oh, so fib is a function that take an int and returns an int. Which is the input and which is the output?" Or "Why don't we name the input variables and the output variables in the function type?" David even asked "Why can't we have more than one output variables?"

Those who learned Ruby closure syntax quickly recognized the resemblance between the "{ int n => ... }" and Ruby's "{ n |... }" . But some reacted violently to the newly coined token "=>". Brian is offended by it enough to come up with his own alternative using the # character instead.

The problem with ".invoke" are revealed by questions like "The .invoke() must be a method of fib. Where is it defined?" At which point an explanation must be given about the generated interface for function types and its invoke() method. Brian went so far as to erase the ".invoke"s from the example. And I do agree that the code looks more natural without them.

Mark's reaction to my quiz included the question about what can go legally into the right hand side of the "=>" in a closure literal. You can't guess it by looking at my example, but a sequence of zero or more statements terminated by semicolons and an optional final expression without the terminating semicolon can go there. And the final expression will be the value of the closure invocation expression.

As long as attitude towards closure proposals are concerned, I'm still seeing mainly two camps: the "I've been programming in Java for ten years, and never once were I wishing Java had closures" camp, and the "If we are going to add closures to Java, which seems inevitable at this moment, let's do a full fledged one like the BGGA closures" camp. There are some switch overs from the first camp to the second camp. Amazingly, nobody is switching from the no-closures camp into the other lets-do-a-little-bitty-syntax-sugar-here-and-there camps.

My personal experience with trying out the BGGA closures prototype is very similar to those of my victims: puzzled a little at first, and one by one, the concepts clicked. And the whole thing doesn't feel that crazy as my first impression suggests. Unlike Brian, I had no problems with the "=>" notation. Maybe it has something to do with my mathematics background where we use "=>" or "->" to mean exactly what the BGGA closures proposal means. For example:

floor: RZ
floor(x) = max { n ε Z | n ≤ x }

However, I do see points in Brian's and now Stephen's proposal. I like Brian's proposal better because it doesn't introduce a new name for the type. Stephen's proposal is like a typedef, where many names could be introduced to represent the same function type.

I do have a better solution. Instead of inventing a new, different way of representing function types, why don't we borrow the notation from a language where functions are first class entities. No, I'm not talking about Haskell or ML or even Scheme. I'm talking about C. Yes, C, as in, the C language where Java inherited a whole bunch of basic syntax.

For those who are not familiar with C, a variable in C can be of a type of a function. And functions can be arguments and return types of other functions. And those three places are exactly where the BGGA closures needs syntax for function types. So why don't we borrow the C syntax with some simplifications.

To designate a variable or field as of the type "{int=>int}", we simply say:

int (*fib)(int);

compare with:

{int=>int} fib;

To designate that a method takes a function type parameter, we simply say:

int apply(int (*fib)(int), int x) {
  return fib(x);
}

compare with:

int apply({int=>int} fib, int x) {
  return fib.invoke(x);
}

Similarly for returning function types:

int (*addn(int))(int) {
  // ...
}

compare with:

{int=>int} addn(int) {
  // ...
}

To initialize a field or a variable with the closure literal, we can say:

int (*fib)(int n) {
  n == 0 ? 0 :
  n == 1 ? 1 :
  fib(n - 1) + fib(n - 2)
}

compare with the definition at the beginning of the post.

Notice all of the above are valid C code and should make Java programmers feel more at home.

If we want to go further along this direction, we can define a closure literal with the following syntax:

(*)(int n) { n * n }

If the thought of using the "*" clouds your mind, we can use "#" in its place.

A caveat: I'm not a language designer, and I'm not proposing anything here. What I said may or may not make sense. To quote Dennis quoting Dennis Miller, I could be wrong!

But I don't like the idea of Stephen Colebourne having all the fun! ;=)



Oh please no.

A declaration in java is
  TYPE identifier
I would hate to return to C days when the identifier is buried in the middle of a complex type expression.
  BIT_OF_TYPEidentifierANOTHER_BIT_OF_TYPE
For the same reason I dislike that java persists in supporting C-style array declarations...
  TYPE identifier[]

Oh please no.

We can move the identifier to the right of the function type, just like what Java did to the array types:

int apply(int (*)(int) fib, int x) {
  return fib(x);
}
int (*)(int) addn(int n) {
  return (*)(int x) { x + n };
}

KISS

Add to java.lang:

interface Method0<R> { R call(); }
interface Method1<R,A1> { R call(A1 a1); }
...

Then we can simply program to the Method interfaces. Nothing new needed.

Its also a good idea to drop wildcards in this context:

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

Re: Dreaming Up Syntax For Function Types For BGGA Closures

Assuming one buys into function types, the BGGA syntax is tough to beat. Imagine a method signature distilled to its essence.

In particular, note that it can include a throws clause:

{ String, String => String throws IOException }

Note how the entire signature of the function is contained in the {}. The function takes 2 Strings, returns a String, and declares an Exception. That's elegant, IMO. Bonus: with Scala's buzz, it's trendy too ;-)

I'm not sure that I see where the throws clause would fit into the C syntax, or if that would pass a similar hallway test.

In general, it is seductive for all of us to tweak the various proposals. However, as I do so, I discover how well thought-out the various proposals are in their own right.

Criticism is healthy, but easy.

Design is hard.

Re: Dreaming Up Syntax For Function Types For BGGA Closures

Which is why I don't call my musings a proposal, but a dream. As I said, I don't have an allergic reaction to the "{int=>int}" syntax. Once understood, the syntax simply flows from my fingertips.

However, it is different. And it does take some explaining. And a bunch of Java developers are allergic to it. The alternative C based syntax does have the advantage of being familiar to a lot of developers. And it hangs naturally with the existing Java method definition syntax.

Re: Dreaming Up Syntax For Function Types For BGGA Closures

With the C syntax, a closure that throws exceptions are declared just like a method that throws exceptions:

String (*closure)(String str1, String str2) throws IOException {
  throw new IOException();
}

Re: Dreaming Up Syntax For Function Types For BGGA Closures

I can see the example of a function definition, but what about passing it...

int apply(int (*fib)(int), int x) {
 return fib(x);
}
For the new function type, would become this: (?)

int apply(String (*fib)(String, String) throws IOException, int x) {
return fib(x);
}

Just my $0.02 but I think the BGGA is easier to pattern-match/scan for the reader. Like generics, it's not pretty at first but, as you say, it flows after awhile.

Weiqi, you make an excellent point with the colouring: I have not yet heard anyone talk about IDE support for the proposals. That could really help with many of the pitfalls. Though it is a worthwhile argument if a syntax should rely on the "3rd dimension" of syntax highlighting.

Re: Dreaming Up Syntax For Function Types For BGGA Closures

Iteresting idea, but I do find it rather confusing. The brackets and * signs don't work for me (I didn't do much C). I'd rather have BGGA function type declarations than these.

Re: Dreaming Up Syntax For Function Types For BGGA Closures

Do you mean the parentheses and the * surrounding the name of a closure? That's exactly the C syntax for function pointers. The parentheses are actually not needed in Java so we can get rid of them. I myself am not too keen on overloading the * token the way C and C++ does either so we can replace the * with everybody else's favorite unused operator #.

So a field or variable of a function type will be declared like this:

int #fib(int n);

A field or variable of a function type will be initialized like this:

int #fib(int n) {
  n == 0 ? 0 :
  n == 1 ? 1 :
  fib(n-1) + fib(n-2)
}

A closure literal is just a closure without a name and a return type:

#(int n) { n * n }

It tells that part of the closure story that says "a closure is just like a method" with minimal explanation because the syntax is just like that of methods. We can say, for example, "The # sign makes it a closure."

Re: Dreaming Up Syntax For Function Types For BGGA Closures

I'm not sure that...
int #fib(int, int)
is better than...
#(int(int,int)) fib
And if anything, the latter is closer to java conventional syntax than the former since this is a declaration of 'fib' and the type of fib is clearly separated to the left.
I'd have loved to factor out the outer parentheses but unfortunately they're needed in method signatures that include exceptions. otherwise this reads quite well without any mental nested bracket counting...
#int(int,int) fib

Re: Dreaming Up Syntax For Function Types For BGGA Closures

It seems to me that once we start to "tweak" the C syntax, we are into the inventing a new syntax territory. And as Michael Easter pointed out, as new syntax go, {int=>int} is just as compelling as #(int(int)) or int #(int), maybe more so.

As for the claim that it is more Java like to put the type on the left of the name, with "int[] ints;" preferrable to "int ints[];", I see it differently. I'd say it is more Java like to declare methods this way:

int fib(int n) {
  // ...
}

and, since closures are most like methods, it is in fact more Java like to declare variables of closure types in a syntax that is more like the syntax of methods.

Re: Dreaming Up Syntax For Function Types For BGGA Closures

Sorry last comment is unreadable.
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);

	// A closure with an exception
	Boolean[Integer i] numberEqualsThree = new Boolean[Integer i] throws IllegalArgumentException { return i == 3; };

	// 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;
		}
	}
  }

}

Re: Dreaming Up Syntax For Function Types For BGGA Closures

I think I agree with the spirit of what you outlined. My musings are for an alternative syntax for BGGA closures. I wouldn't want to alter the meanings of "return" and "this" when they appear in a closure literal.


Add a comment Send a TrackBack