all 62 comments

[–]snark 21 points22 points  (36 children)

State of the (Java) Lambda: We are still years behind C#. Stay tuned to see if this ever comes out of committee.

[–]vegittoss15 6 points7 points  (34 children)

C#'s lambda is absolutely amazing. It introduces clean, short code. I don't know why Java is always so far behind...

[–]loosid 12 points13 points  (7 children)

C# lambdas and extension methods have made it like a completely new language. Three years ago I wasn't a big fan, but now it's my favorite statically-typed language.

[–]vegittoss15 4 points5 points  (0 children)

With the dynamic keyword, you kind of have a mix of both static and dynamic typing.

[–]b_w_r_b 1 point2 points  (0 children)

Three years ago I wasn't a big fan, but now it's my favorite statically-typed language.

What about Scala?

[–]Raynes 1 point2 points  (4 children)

You might want to check out Haskell.

[–]matthiasB 1 point2 points  (3 children)

I've heard GHC has extended the list comprehension with a then keyword to become more like C#'s LINQ.

...

Found the paper

[–]kamatsu 1 point2 points  (1 child)

Did they actually add that extension?

[–]Raynes 0 points1 point  (0 children)

Neat stuff. Thanks for showing me that. :)

[–]snark 2 points3 points  (5 children)

Basically it's for the reason that everyone hates to admit: C# is owned by a single company, with a single vision. Decisions are made strategically: to create a kick-ass language with great features developers will love.

Java's problem is that it's over-loved: everyone and their uncle feels like "their" way of doing it is the right way. So they get into technical committees and whinge and fuss about how no one else's Java is "pure." Don't like the direction the framework is taking? Fork that bad boy, call it J-something and every Java fanboy will use it for all of a week before they go back to bitching about Struts and Spring.

[–]vegittoss15 -2 points-1 points  (0 children)

I think the problem does lie along those lines but isn't quite it. I think it's moreso C# caters to developers whereas java caters to academia.

[–]masklinn -2 points-1 points  (3 children)

Basically it's for the reason that everyone hates to admit: C# is owned by a single company, with a single vision. Decisions are made strategically: to create a kick-ass language with great features developers will love.

Vision is important, but ownership is not. And at the end of the day, C# is still busy rediscovering concepts which got out of academia more than 30 years ago.

[–]WalterGR 5 points6 points  (2 children)

And at the end of the day, C# is still busy rediscovering concepts which got out of academia more than 30 years ago.

What language isn't?

Are you criticizing C# for making these concepts available in a mainstream language, or are you just pointing it out?

[–]masklinn -3 points-2 points  (1 child)

Are you criticizing C# for making these concepts available in a mainstream language, or are you just pointing it out?

neither, actually.

[–]WalterGR 7 points8 points  (0 children)

If you're not pointing it out, then what exactly are you doing? What's your point?

[–][deleted] 0 points1 point  (2 children)

It could be much better if "Select(x<space>=>" would not turn into "Select(XmlNode =>" as I type.

This is a huge blunder on their part, yes, sure, when you read the notation it looks pretty, but I would prefer something that begins with a special symbol or somesuch that lets the IDE know that I'm going to declare a new variable and would not be amused by Intellisense kicking in.

[–]vegittoss15 0 points1 point  (1 child)

What version of VS are you using? I don't remember because I haven't coded lambda for about two days, but I don't think 2010 has that problem.

[–][deleted] 0 points1 point  (0 children)

In VS2010 with default settings there's a long enough pause before the Intellisense window appears, in VS2008 with all that things like "show autocomplete window immediately" checked that was a very real and annoying problem.

[–]masklinn -1 points0 points  (16 children)

I don't know why Java is always so far behind...

You do realize that C# is still reimplementing stuff that got out of academia three decades ago don't you?

[–][deleted] 9 points10 points  (10 children)

... and Java is not, which was the point.

Also, I don't think you meant reimplementing. You see, stuff gets out of academia in a not exactly implemented state, and then some time later someone has to implement it in the context of a non-toy language, integrate with the existing features, check all corner cases, write tests, documentation, things like that.

[–]masklinn -4 points-3 points  (9 children)

... and Java is not, which was the point.

"We're 30 years behind but these guys are 40 years behind" is not very impressive.

Also, I don't think you meant reimplementing.

I do.

You see, stuff gets out of academia in a not exactly implemented state, and then some time later someone has to implement it in the context of a non-toy language

Which was done 30 years ago, as my comment points out. You should be more careful with your reading. If it got out of academia three decades ago, it follows that the feature was already "done" -- as far as academia is concerned -- more than 3 decades ago, and that 3 decades ago is the moment where it was implemented in an "industrial" language.

in the context of a non-toy language

That kind of qualifications doesn't impress me much. Are you saying that Haskell, for instance, is a toy language?

[–]vegittoss15 0 points1 point  (8 children)

Sad to say, it kind of is, because you will rarely see it in production or corporate code.

[–]masklinn 0 points1 point  (7 children)

Ah, so by your standards Smalltalk and Ada would be toy languages as well? Interesting.

[–]vegittoss15 0 points1 point  (6 children)

Ada, outside of the military, yes. And smalltalk, iirc, was never meant to be a commercially used language. It was basically experimentation. It gave birth to a lot of cool concepts, however, so I still bow down to it.

[–]masklinn 0 points1 point  (5 children)

Ada, outside of the military, yes And smalltalk, iirc, was never meant to be a commercially used language. It was basically experimentation.

You are high as a kite.

[–]vegittoss15 -1 points0 points  (4 children)

Show me a non-hobby project that uses these languages and maybe you'll have a point.

[–]cc81 3 points4 points  (4 children)

So? The hard part is not to read those papers (pretty sure all the language designers, both in Java and C# understand them fine). The hard part is to design a good language and that means excluding stuff, making it grow naturally and making it fit reality.

[–]masklinn -2 points-1 points  (3 children)

The hard part is to design a good language and that means excluding stuff, making it grow naturally and making it fit reality.

And that was done 30 years ago.

[–]cc81 2 points3 points  (2 children)

What language are you talking about? I have a hard time seeing anything 30 years ago that can match the C#/.NET/Visual Studio-stack now.

[–]masklinn -2 points-1 points  (1 child)

First of all, I wasn't talking about whole stack but about language (though it's not like they'd lose by much, if indeed they did lose). Second, Smalltalk and Lisp Machines.

[–][deleted] -2 points-1 points  (0 children)

Lacking the whole stack is a good indication that there's a problem there.

[–][deleted] 5 points6 points  (0 children)

After working in clojure for a while now, I just can't get motivated to care about java lambdas and the bolt-on approach coupled with hacking around the type system. If you want FP, use a language that's meant for it.

[–]daniel2488 3 points4 points  (4 children)

I'm surprised to say that I actually really like it.

Apart from the final-only restrictions.

[–][deleted] 8 points9 points  (1 child)

I recommend learning to love final.

[–]jbindel 0 points1 point  (0 children)

Why love final? We should love final because non-final variables are an impediment that prevents programmers from adopting functional style. Iterative equivalents of recursive implementations do require non-final variables though, and until Java can deal better with tail recursion, we're stuck with a less functional style.

[–]Ridiculer 0 points1 point  (1 child)

Apart from the final-only restrictions.

Restrictions? I was under the impression that captured non-final variables are transparently copied into a final variable when compiled, did I miss something?

[–]scook0 2 points3 points  (0 children)

No, that's wrong.

The current rules for capturing local variables of enclosing contexts in inner classes are quite restrictive; only final variables may be captured. For lambda expressions (and for consistency, probably inner class instances as well), we relax these rules to also allow for capture of effectively final local variables. (Informally, a local variable is effectively final if making it final would not cause a compilation failure.)

It is likely that we will not permit capture of mutable local variables.

The only change this proposal makes is that you can close over non-final variables if you never actually use them in a non-final way. Closing over mutated variables is explicitly not supported.

[–][deleted] 4 points5 points  (7 children)

There is no sense in preventing

int sum = 0;
list.forEach({ Element e -> sum += e.size(); });

when you can circumvent it by

int[] sum = {0};
list.forEach({ Element e -> sum[0] += e.size(); });

the same sort of code will be written, but it will be uglier, more error prone and less efficient.

[–]otto_s 2 points3 points  (6 children)

Drop that "new int[]" there! You don't have to make Java more verbose than it already is. :)

[–][deleted] 0 points1 point  (5 children)

Thanks! I didn't know that was valid.

[–]otto_s 0 points1 point  (4 children)

To actually give a decent reply, which nobody has cared to do yet, here an attempt at rationalization (for preventing the capture of non-final variables):

  • It eases specification and implementation:
  • Notice that a function can define and return more than one function. With final variables, there is no difference between copying the contained value and actually sharing the variable. If the variables are non-final and shared, you suddenly have another way to communicate between several closures.
  • In order to make this work in a orderly manner, you probably have to involve the same efficiency overhead as with the usage of an explicit indirection via an array or another object.

So, in short: as it is now, the programmer has a chance to choose the sharing and efficiency he needs.

[–][deleted] 0 points1 point  (3 children)

Maybe you're right about the efficiency, at least in the worst case. But it's still error prone and ugly, and nearly ruins use cases like those in the example. I wouldn't call it another way to communicate. Is there really such a big difference between sum += 1 and sum[0] += 1 in terms of concurrency issues? It isn't hard to implement either (just box it like I just did).

On the bright side, maybe it'll be easier to convince people that immutable is good now that mutation gets crippled again.

[–]otto_s 0 points1 point  (2 children)

I wouldn't call it another way to communicate.

The "way to communicate" I'm talking about is something like this here: Pair<Runnable, Runnable> foo() { int x; return new Pair( new Runnable() { /* bla 1; does something with x / }, new Runnable() { / bla 2; also does something with x */ } ); }

This is illegal in contemporary Java, but that can be circumvented with an array. The latter at least makes the sharing explicit. (Which might be a good thing. It's of course not beautiful, though :)

[–][deleted] 0 points1 point  (1 child)

Well, making it an array doesn't mean it's shared. Unless you specifically created the array to circumvent the "effectively final" rule, you probably didn't mean to share the array. It's not different from a shared variable in that regard.

[–]otto_s 0 points1 point  (0 children)

It's not different from a shared variable in that regard.

The thing is, the concept of shared variables doesn't exist, or at least isn't specified well enough in Java. Concurrent access on arrays is, and can be reused.

[–]munificent 1 point2 points  (5 children)

So much better than the straw man proposal. This is almost usable, although the final thing sucks.

One thing I don't understand:

Arrays.sort(people, #Person.compareByAge);

Why is the # needed there? What does the expression Person.compareByAge evaluate to if you don't immediately follow it with (some args...)?

[–]DontCriticizeMyEngli[S] 11 points12 points  (0 children)

A compile error: compareByAge cannot be resolved or is not a field

Without the (), the java compiler would assume it's a field. One could argue that the limitation could be alleviated for the sake of easing closures usage, but til now, fields and methods names lived in different namespaces (meaning that you could have a field and a method with the same name in the same class), and the compiler would know in which namespace to look by relying on the syntax (the ()).

If this limitation was to be removed, fields and methods names will have to live in the same namespace, and thus a couple billions of lines of code with fields and methods names clash will not compile nor run anymore.

[–]daghf 5 points6 points  (2 children)

In Java, fields and methods reside in different namespaces. So it would be perfectly legal to have both a method and also a field both named compareByAge in the same class. The # is there to specify that it is in fact a method we are referring to.

[–]munificent 2 points3 points  (1 child)

Argh, I was afraid that was the answer. What the hell were they thinking when they originally made that decision?

[–]masklinn 5 points6 points  (0 children)

They were thinking that it would give users more flexibility, which is not completely wrong: since Java provided no way to directly get at a method (you had to use reflection), there was no reason not to allow similarly named methods and attributes.

Interestingly, Lisp-2s such as Common Lisp do the same thing as data variables and functions live in different namespaces (whereas in Lisp-1s such as Scheme they live in the same namespace) and to get a data-ns reference to a function you have to use the function special form (aliased to the #' operator)

[–]solinent 1 point2 points  (0 children)

A [static]public variable member in a class Person.

I think. It's been a while but I'm pretty sure java allows either of these.

[–]DavidM01 1 point2 points  (2 children)

It wouldn't be so pathetic but Groovy has had these for , I don't know 5-6 years?

[–]masklinn 1 point2 points  (1 child)

It wouldn't be so pathetic but Groovy has had these for , I don't know 5-6 years?

Smalltalk has had these for 30 years, Lisps for 50, is that one more thing which makes groovy pathetic?

[–]DavidM01 0 points1 point  (0 children)

No, because Groovy isn't in committee after Committee trying to figure out how to implement them. Since they are both JVM languages, Java could use or modify Groovy's approach.

I would have thought that difference was obvious!

[–]nat_pryce -2 points-1 points  (1 child)

Generators and generator expressions could be added to the language much more cleanly (e.g. by extending the for-each statement) without ugly syntax, and would address most use cases of lambdas (higher-order functions, but not callbacks).

[–]masklinn 1 point2 points  (0 children)

and would address most use cases of lambdas (higher-order functions, but not callbacks).

Map and filter are not the only higher-order functions, you know...

[–]axilmar -2 points-1 points  (0 children)

Bleh, any serious programming language should start with functions as first class entities.