Monday, 12 February 2007

Java language - anonymous methods (CICE)

In my last entry I wrote about method adaptation as an alternative to closures. What if we extended the syntax there to allow anonymous methods?

Anonymous methods

Using the example from last time, we had created a method adaptor that looked as follows:

public void init() {
  executor.execute((Runnable->run()) this->processTask());
}
private void processTask() {
  // lots of code to run in the executor
}

Here, the this->processTask() syntax is being automatically adapted to the required Runnable interface. But often, we don't want to create a separate method for this, so lets create the method anonymously:

public void init() {
  executor.execute(Runnable->run() {
    // code of anonymous method to run in the executor
  });
}

This would operate in the same way as an inner class, except with one method. In other words, the contents of the anonymous method has the same rules and restrictions as a method does when written within the normal inner class syntax. Hence, 'return' will return from the anonymous method, not the surrounding init() method. Thus, the above is merely a exact shorthand for:

public void init() {
  executor.execute(new Runnable() {
    public void run() {
      // code to run in the executor
    }
  });
}

The code can only access final variables as with inner classes. But, that should be addressed for both anonymous inner classes and anonymous methods separately.

For the sake of completeness, here is an example with parameters:

public void init() {
  Collections.sort(list,
    Comparator<String>->compare(String a, String b) {
      return a.length() - b.length();
    }
  );
}

For those of you following the Java language debate, this is of course just an amended version of the CICE proposal.

Compared to BGGA

So why consider this route (method adaption and anonymous methods) rather than full blown BGGA closures?

Firstly, it is less of a jump for existing Java programmers. This is simply a slightly shorter syntax for creating an inner class. I suspect many Java developers will appreciate that. This change also works well together with method adaptation, which provides an alternative way to refactor the common case of the single line inner class that calls another method.

Secondly, it is fully and clearly documentable. The parameter being passed to the executor or the sort method is a real interface, which can be documented in all the detail needed. Documenting a BGGA closure method will have to be done for every definition.

Thirdly, there is no confusion over different syntaxes depending on whether its a synchronous or asynchronous use-case. There is no need for a hacky RestrictedClosure interface.

Finally, it feels more correct to be passing an object, rather than an arbitrary block of code, to a method that processes it at some later point in time. An object is expected to have its own lifecycle - whereas a developer expects a block of code to be executed immediately.

However, there are downsides too. Firstly, anonymous methods are less powerful. There is no ability to have function types, and the functional programming styles that accompany them.

Secondly, anonymous methods are more verbose. The BGGA proposal presents examples that are extremely short and pithy, including for executors. Except of course that the rules for what code you can put in that block differ from other blocks.

Finally, the synchronous use cases are not served that well by anonymous methods. The control statement form of BGGA is very expressive of the intent of the coder in those cases (for example looping around a map, locking or working with files).

Summary

What is my opinion? Well I haven't quite made up my mind yet. I should be a natural supporter of BGGA, as I am in favour of pushing Java forward. But BGGA bothers me in lots of annoying little ways.

So, consider my blogs on anonymous methods and method adaption as my attempt to explore an alternative to BGGA. Comments welcome as always.

10 comments:

  1. very nice! but i really don't like -> notation. Brings back bad memories ;) can't you use some other single character?

    ReplyDelete
  2. Something like follow?

    public void init() {
    executor.execute((Runnable.run()) this.processTask());
    }

    It's a crazy idea in anyway.

    ReplyDelete
  3. Thats pointless. The only advantage of using closures is the possibility of creating function composition and frameworks around that. Why function composition? because functions are the primary unit computation (you can see the seen on the runnable thing that only does run()). The other principal advantage in my mind is partial evaluated functions, that unfortunately it seems no proposal is yet comtemplating. This would be immensilly usefull.
    As an appart if java could make a NULL type for any class, that each function of that class returns a NULL type, it would remove the pointless drugery of checking for null, nullpointerexception and still allow null for function control purposes.

    ReplyDelete
  4. Stephen Colebourne12 February 2007 at 15:06

    @Evan, I like the -> but I'm not hung up on it.

    @Paulo, Not everyone agrees that functional programming is the way for Java to go. No doubt it has benefits, but it is a very different from the Java of today. If you want FP, why not use one thats designed for FP?

    ReplyDelete
  5. If the language does not evolve there is going to be interface duplication and general framework abuse in java for the future. For example why does Runnable exist at all? It's a function and can only be called thus (no smart encasulation here). Another example but more annoying: there is no Interface in the jdk for call(T whatever). This means that to make a "framework", that tries to hide pointless things like, the file should be accepted, etc we have the brain stress of javax.swing.filechooser.FileFilter objects. In another framework offcourse this could be anything else that did exactly the same thing and yet as the object had another name, not interchangable. Wouldn't it make more sense to give the interface that each function has by default: its arguments and return value, that is general to each and every function that accepts those arguments and returns that value?

    ReplyDelete
  6. Hm, am I chronically negative? I don't know.
    Firstly, I wonder why you say that what you present is an amended version of the CICE proposal, as I cannot see any functional difference, just a different notation (you like thin arrows, don't you? ;)). Secondly, as you said yourself, CICE only provides some shortened syntax to define anonymous classes having only one method (SAM interfaces and classes), which I would call another "save me some keystrokes" proposal. Of course, it's a nice-to-have as it makes the code a bit more readable. But that's about it. Wonder if such a feature would support or rather counteract good design, as it may hype to build SAMs all over the code (like the ThreadLocal example, building abstract classes only to be able to use it as anonymous method instantiable class).
    Of course, CICE does not have to differenciate between synchronous and asynchronous use-cases, as it only supports the restricted case anyway.
    Why does one see closures as a feature of functional programming language only? If one has been using Smalltalk for a while, a closure is as natural an object to work with as any other, only that it contains lexically bound executable code. Inner and anonymous classes do this partially, as it is allowed to reference to the enclosing class's instance and members (and final locals).
    Of course, BGGA has some restrictions and downsides. Some of which, I don't like, too. But it provides far more powerful features than simply simplifying anonymous object construction.

    ReplyDelete
  7. Stephen Colebourne12 February 2007 at 20:34

    @Stefan, This differs from CICE because it specifies the method name being implemented. This means that you can override swing adapters like MouseAdapter, and the thread local example without needing arbitrary abstract classes. In other words, you're not limited to SAMs.

    On the FP point, I'm not disputing that closures are neat in many ways, I'm just questioning whether they are appropriate for Java. I haven't made up my mind yet, and I suspect my next post will emphasise that.

    ReplyDelete
  8. "This means that you can override swing adapters like MouseAdapter"
    Ok, that is not made clear nor stated in the post, if I can trust my eyes. Maybe, you should have shown this as an example. I cannot tell about the situations, where one only wants to override one of many methods in an adapter. From my experience, this is very seldom the case (for non-SAMs).
    Comparing CICE (or ColeCICE) with closures in my opinion is otiosely. They target different audiences. While CICE only tries to make existing anonymous classes easier to use, closures bring new features into Java (and, of course, more obstacles to deal with). Maybe I'm too indulged in Smalltalk.
    What about a counter proposal on closures taking all the things in which you miss or out that annoy you. There is too much fiddling around with little changes, which do not provide a big step forward but introduce new syntax restricting future enhancement possibilities. Introducing new stuff just to "save keystrokes" or "make it more readable" are nice, but don't push a language forward. If it were for that, we would no longer be using Java for enterprise software development, but maybe Ruby, Groovy, or Scala.
    The neat thing on closures is its potential to allow language extensions like functionality to be handled as API issue, rather than having to change the language's syntax over and over again. Maybe it would be better to introduce some closure type, as for documentation, or method-accessors to match closures with signatures or to define "method types". I thought about what name I would give a function type like {String=>int}, but couldn't find something useful, so maybe it's better to keep anonymous for not implying a semantic (e.g. valueOf) the actual closure will not have.

    Sorry, for dumping my brain on your blog. I did not mean to convince you on closures, but tried to put another opinion to your thoughts. Hope it's somewhat conducive and not annoying only.

    ReplyDelete
  9. Stephen Colebourne12 February 2007 at 23:54

    @Stefan, Sorry it wasn't clear that it wasn't just SAMs.

    I have made the point elsewhere that CICE doesn't compete with BGGA in what it is trying to achieve. Its easy to get into a semantics debate about what is and isn't closures.

    I am trying to move towards a counter-proposal to BGGA. But I don't want to publish the next step until I've thought about it a little more. Because, I do agree that being able to add language extensions should be a goal too. ie. this blog post only represents half the picture formulating in my brain.

    ReplyDelete
  10. I too am not a fan of the -> notation. Reminds me of traumatic Perl experiences I am trying to block out. :)

    Dr.E
    Appetizing Java Programming Report:
    http://www.turingshop.com/reports/01Java/

    ReplyDelete