Summary
Closures are sometimes used as control structures in code. The latest update to the Java Closures JSR proposal includes support for such control abstractions, as well as for restricted closures and function types. Neal Gafter explains these updates to the Closures for Java proposal in a recent blog post.
Advertisement
Closures are a popular programming tool in many languages. While Java does not yet provide closures, a group of experts, led by Neal Gafter, are working on a proposal to add closures to Java. The proposed specification can be found at JSR Proposal: Closures for Java.
[When] you're writing a method intended to be used as a control API, you can write a function type with the (new) ==> token to designate an unrestricted function (interface) type. Let's do that to write a method, with, that will automatically close a stream for us.
We'll have to see how it plays out in action, but my gut feeling is that control statements should be defined at the language level (where they have more access to compiler context and specialized logic) and not left up to library developers. I have a feeling that including this feature will cause a blowout in java dialects, with dubious control statements being introduced that are more specialized than they are worth.
That being said, I think the "with" (aka "using") construct, along with an IDisposable-like interface for objects to implement, is a reasonable addition to garbage collected OO langauges. It gives us something similar to the stack-allocated object/destructor pattern in C++, which is a nice way to deal with non-gc'd resources that have a stack-centric lifecycle. The Powers That Be should consider adding that feature independently of any closures proposal they consider. (Bloch advocated exactly that in his closure controversy talk.)
It seems like Gafter thinks of control statements as a sine qua non of his closures proposal. To me, it really seems like a very specialized, peripheral feature that is vanishingly insignificant when compared to the value of closure-based operations over data structures.
> We'll have to see how it plays out in action, but my gut > feeling is that control statements should be defined at > the language level (where they have more access to > compiler context and specialized logic) and not left up to > library developers. I have a feeling that including this > feature will cause a blowout in java dialects, with > dubious control statements being introduced that are more > specialized than they are worth. > <snipped> > Cheers, > Carson
I agree that these user-defined control statements will in many instances not look like proper "language level" features to the casual reader - but that's not necessarily their goal is it?
I had thought one the largest benefits will be found in application / company specific code where it does a fantastic job of eliminating redundant / duplicated blocks of code everywhere.
Things like managing mock objects in unit test blocks might offer just as much savings in reduction of code duplication (or class extensions) as the more lofty concurrency library / file IO examples floating around. It doesn't sound as sexy but it's impossible to easily follow the cardinal rule of DRY in java as it is today.
Perhaps the burden of making sure your team doesn't abuse language features is on the team itself. There are of course numerous existing language features that are and can be abused every day already, but no one complains about those language features because they've always been there.
I had thought one the largest benefits will be found in application / company specific code where it does a fantastic job of eliminating redundant / duplicated blocks of code everywhere.
Yeah, I guess. I just think plain old closures get you 90%+ of that benefit without causing a dialect blowout.
Maybe you are right, hard for me to tell right now. As I've grown fond of this language extension ability present in other languages (for me in ruby / javascript) - I'm naturally inclined to defend and support this particular feature as I've used and personally seen it benefit code bases substantially.
Combine that with static typing and a kick ass virtual machine and there may be hope for those of us envious of other languages features but having to sit with our hands tied because it's not always logistically sane to build out libraries of certain sizes without a little more help from compilers and tools along the way to make changes. ....which I've also personally seen the result of in large dynamic codebases. The end result is always the same, stagnation because people are afraid of breaking things.
> I had thought one the largest benefits will be found in > application / company specific code where it does a > fantastic job of eliminating redundant / duplicated blocks > of code everywhere. > > Yeah, I guess. I just think plain old closures get you > 90%+ of that benefit without causing a dialect blowout. > > Maybe I'm missing something. > > Cheers, > Carson
Actually looking at the example code, I'm completely confused about the exception handling. You catch and log exceptions from close(), the least common possible place to have an exception.
The more common and IMO more important IOExceptions go where??? How are they declared?? Shouldn't the with closure declare throws IOException??? And inside the with, where the heck does logger come from? What if the code calling your closure has a LOGGER or uses a Java logger instead of a log4j logger or vice versa???
I like the fact that he attempts to tackle a "semi-real" example, but if something as simple as this is completely foobared what can I expect from a difficult example?
Really, stuff like this is why Java will continue to become irrelevant. Attempts to add basic language features like closures become mired in an attempt to add the kitchen sink (like with wildcard generics) instead of trying to provide a clean, easy-to-understand syntax that does 90% of what you want. Honestly, do we need ==> and => ? Do we need an @Shared annotation? Do we need to worry about returning from a closure somehow magically returning from the enclosing scope? There are standard ways to work around all those things already, and they just come up so rarely when you're actually using closures in day-to-day work that it's not the important part.
I'm not saying they don't solve real problems, they just don't solve the important problems, and they make understanding the code harder. Why not just basic closures with the same scoping, exception-handling, and return-semantics as anonymous-inner classes? That would get me 90% of what I want and let me write simple code much more elegantly. Would some type-inference while they're at it be too much to ask?
This seems way too much like what happened with generics: wildcards let you make things "completely" typesafe by closing the typesafety hole that arrays have, but personally I almost never screw that up, and the overhead of dealing with wildcards in generics is huge. I've probably seen an array store exception twice in the last 5 years; it doesn't seem worth it to add a ton of complexity and overhead to my work to try to catch something at compile time that I screw up every 2 1/2 years.
Likewise, I don't want a language with the kitchen-sink of features that I may or may not have to understand. That's why I can't stand C++; I've never programmed in C++ enough to achieve mastery of it, but my personal experience has been that everyone I know who codes C++ uses some subset of the language features because the whole language is way too difficult to master, and if you have to start dealing with code that uses features you don't understand you've got a lot of learning to do. The same thing happens with Perl, where there are way too many ways to do things; that's great for writing code, but if you're trying to read someone's code and they do things using some crazy set of language features you don't understand, you're going to have a hard time of it.
I don't want to see the same thing happen to java, where there are a bunch of obscure features like @Shared and ==> instead of => and custom control syntaxes that make it impossible to read other people's code, all in the name of saving me from writing two lines of code once a week. Give me the stuff that saves me two lines every 5 minutes (like basic closures and type inference and basic generics) and leave it at that.
> Likewise, I don't want a language with the kitchen-sink of > features that I may or may not have to understand. That's > why I can't stand C++; I've never programmed in C++ enough > to achieve mastery of it, but my personal experience has > been that everyone I know who codes C++ uses some subset > of the language features because the whole language is way > too difficult to master, and if you have to start dealing > with code that uses features you don't understand you've > got a lot of learning to do. The same thing happens with > Perl, where there are way too many ways to do things; > that's great for writing code, but if you're trying to > read someone's code and they do things using some crazy > set of language features you don't understand, you're > going to have a hard time of it. >
I think it's fair to say that in Java, too, most developers use a subset of the language's capabilities. That's just fine with them. New features don't really force people to use them. But, if they are there, they allow those who need those features to keep effectively using the language. That's especially the case with those who write APIs for other's to use.
> I think it's fair to say that in Java, too, most > developers use a subset of the language's capabilities. > That's just fine with them. New features don't really > force people to use them. But, if they are there, they > allow those who need those features to keep effectively > using the language. That's especially the case with those > who write APIs for other's to use.
You can always make the arguments that 1) no one uses all the features in a language and 2) if you don't like it you don't have to use it. But that doesn't mean it's a good idea to slam every imaginable feature into a language; even if I don't use the feature myself I still have to understand how it works so I can understand other people's code when they use it. Adding language features only fails to impact me if I never have to read or understand any code that uses them whether or not I choose to make use of them myself. It's a judgment call and a matter of personal taste, of course, but I really don't think it's the case that any language feature that could possibly be useful should be included in the name of making the language "more powerful." Simple and powerful is almost always better, in my book, than complex and marginally more powerful.
What I want out of a language is the ability to write code clearly and concisely; fancy language constructs like Lisp macros can help with the "concise" part, but they don't always help with the "clear" part. Programmers love being clever and like solving problems elegantly and fancy language constructs appeal to those desires, but it's almost always the wrong thing to do in my experience since clever solutions or super-targeted language features just add complexity. I'll take 200 dead-simple lines of code over 100 complex lines of code any day, and I hate having more code. I'll take 100 dead-simple lines of code over either one, though, which is why I want some kind of closures in Java.
To me, this incarnation of the closures proposal crosses the line by adding in a bunch of complicated stuff that I'll never use, is easy to screw up and is hard to understand. Hence, I think it's a bad idea and would prefer a simpler proposal. Language design is all about aesthetics and taste, and personally I just don't like the way a lot of the recent Java features have been designed.
@Java-dialects through control abstraction Looking at how APIs are written nowadays, using domain-specific libraries already provide their own "dialect". Only common interfaces may match amongst different APIs. But one always has to get to know the domain-specific classes and methods and how they play together in each of the libraries. When developers get used to the control-invocation-style for accessing an API, it will not differ at all wrt. using a library. Although, IDEs usually are no argument (only I don't think the average developer won't use one), here, the different coloring of method invocation vs. language constructs allows easy distinctions.
@Closure proposal Unfortunately, most discussions on closures in Java deal with the BGGA proposal and rather reject it instead of looking for alternatives. With First-class Methods we have another proposal for closures on the table that also has a related control abstraction proposal (see http://docs.google.com/Doc?docid=ddhp95vd_0f7mcns). Our aim is to get the right mix between power and usability (the magic 80:20 rule). The main difference to BGGA is that FCM proposes inner methods, which relate to restricted closures (i.e., no break, continue, return to the enclosing scope), and JCA can be seen as an application of unrestricted closures. A mix of both is not allowed, as we think they target different purposes.
Interesting, thanks for posting the link . . . since I'm used to looking at closures in dynamically typed languages or languages with type inference the examples you give look overly verbose to me, but that's more of just a failure of Java to provide type inference.
At first I thought that I wouldn't really need to be able to refer to methods at compile time as first-class functions, but then I remembered that I end up writing a lot of reflective code that requires dynamically lookup up fields or methods, and this would let me do that in a compile-time safe way, so I can see the utility better now. It also seems much cleaner to just say that anonymous functions obey the same scoping rules as methods on anonymous inner classes; scoping is one of the few things that I think Java does pretty right because it's so dead-simple that you're never wondering what's going on, so I really don't like the idea of making it more complicated, which Grafter's proposal would do.
I like the idea of separating control abstractions from the closures themselves as well; they seem pretty separate to me too, and one thing that really bothers me about Grafter's proposal is that it uses some pretty nasty tricks to weld the two together. The idea of having "restricted" versus "unrestricted" closures based on where you use => or ==> just makes my skin crawl.
The control abstraction and closure issues are related, but I think that if we just had closures people wouldn't necessarily care as much about control abstraction, because normal, restricted closures just work well enough and are understandable enough. IO.eachLine() in Ruby works well enough for me without any fancy control abstraction syntax; you can pretty easily just define the method such that it either handles exceptions thrown by the closure or rethrows them, and while it's not the same as full-on control abstractions it's close enough and clear enough what's going on. Returning from within a closure should always just return from that function and never the enclosing scope, and ditto for exceptions thrown from the closure. Let the person invoking the closure figure out how to handle them; it's not rocket science. The only reason you'd ever want something else is if you're trying to wedge closures sideways so you can use them for language control abstractions too, which just isn't a good idea.
One thing that's always bugged me is the way that the new for loops in Java 1.5 throw NPEs if the Iterable is null; I use the for loops all over the place because they're so useful, but they make the debugging that much harder because an NPE is getting throw from a line of code with no method calls on it and you have to understand how that's being implemented by the compiler. Adding more control abstractions with confusing exception and return behavior isn't a win for me.
So I'm with you . . . I'm much rather see something like FCM in Java 7.
> One thing that's always bugged me is the way that the new > for loops in Java 1.5 throw NPEs if the Iterable is null; > I use the for loops all over the place because they're so > useful, but they make the debugging that much harder > because an NPE is getting throw from a line of code with > no method calls on it and you have to understand how > that's being implemented by the compiler. Adding more > control abstractions with confusing exception and return > behavior isn't a win for me.
I very much like the for-each loop, only it restricts on objects being Iterable and does some code additions behind the scene, as you stated. This is the same argument that makes me dislike ARM by Joshua Bloch. Making a language construct depend on a library detail (namely an interface) seems like a bad approach to me. In ARM Closable and Lockable would add up to it. And I'm quite sure, such interfaces will be misused sooner or later for "similar" but unrelated situations, which makes code unmaintainable. The nice thing with API defined control abstractions, it becomes easier to maintain; firstly, because of the API is documentable at spot, and secondly, because, if in great doubt, one can still introspect those abstractions being still Java. for-each surely is documented in the JLS, including the transliteration to old style Java. But as a programmer I'd, too, say that having a language construct throwing an exception is surprising at least.
I personally want the full featured version of closures. If I can't have that, I'd prefer none over some half-implemented version.
People point to generics as an argument against adding complexity. I don't see the syntax of generics as a problem; rather erasure is the complexity and the problem I continue to run into. I have to constantly remind myself that generics are half-implemented and, therefore, have limited applicability.
I want a complete implementation of closures and fixed version of generics in Java 7. We have many use cases for both where I work.
I also think arguments about feature abuse are a red herring. There are many ways to deal with that and I think there is no useful language that is entirely safe from abuse. Therefore, let the language be robust, and let the organization deal with personnel issues.
> I personally want the full featured version of closures. > If I can't have that, I'd prefer none over some > half-implemented version.
First-class methods is not a half-implemented version of closures, it's focus is on maintainability and least-surprise-usage of closures in Java.
> People point to generics as an argument against adding > complexity. I don't see the syntax of generics as a > problem; rather erasure is the complexity and the problem > I continue to run into. I have to constantly remind myself > that generics are half-implemented and, therefore, have > limited applicability.
I agree, Generics is greatly missing features like reification. Has nothing to do with closures, though.
> I want a complete implementation of closures and fixed > version of generics in Java 7. We have many use cases for > both where I work.
It would be interesting to see some of these use cases. Especially for unrestricted closures that cannot be dealt with using control abstractions. It's always a bit difficult to image all the applications people think of when talking about closures. Maybe, you could post examples at your blog and link it here?
> I also think arguments about feature abuse are a red > herring. There are many ways to deal with that and I think > there is no useful language that is entirely safe from > abuse. Therefore, let the language be robust, and let the > organization deal with personnel issues.
The abuse argument (at least mine) was targeted on ARM by Bloch. The abuse would not be to apply the features proposed but to misuse them. ARM proposes two language constructs for objects implementing the Closable interface and some Lockable interface. It seems alluring to implement either interface to use these features in a semantically inappropriate way.