Hacker News new | ask | show | jobs
by int_19h 2686 days ago
Java did it way too slow, and that is a significant contributor to it being relegated to "legacy" in many areas. If it waited for the other languages to prototype stuff, it might have not been the case. The problem is that it waited for them to prototype it, refine it, release it, popularize it, and for their community to adopt it, before even starting to work on it in Java - which means that by the time they had it, most people who needed it were already elsewhere (not necessarily off JVM, just another language).

Lambdas were a very good example - if you look at the closest competitor, C#, it got the first take on them back in 2005. Then a major refinement in 2008, adding type inference. By 2010, lambdas were idiomatic in C#. Java, in contrast, released the first version in 2014. And even then, they're still less powerful.

3 comments

Looking around the office and my phone, Java is anything but "legacy".

As for alternative JVM languages, while they are cool and have brought many fresh ideas into the platform, they remain a very tiny portion of the Java developers' market.

Java takes a very long time, because backwards compatibility and cooperation among giant companies takes years.

C# has basically Microsoft deciding how the roadmap looks like and rebooting the platform multiple times.

C# 8 won't even be fully supported on the .NET Framework.

And yet Google is investing into Kotlin.

Yes, Java takes it slow because its target market - enterprises - want it, and they have good reason to. There's nothing wrong with that. All I'm saying is that there are many other niches where developers find lagging too far behind other available options, and there's a noticeable decline in its use because of that.

And so the analogy with PostgreSQL in this case isn't working well.

Google is investing into Kotlin due to politcs with Oracle, and JetBrains (makers of InteliJ which powers Android Studio) pushing Kotlin everywhere they can.

They even separate Kotlin/Native graphical debugging from InteliJ, so that developers get to shell out for Clion license as well.

The only variant where InteliJ and Clion come together is on Android Studio, thanks to us NDK users being vocal how 3rd class it felt versus Eclipse CDT.

On platforms where developers can make use of standard Java there is less pressure to adotpt alternative JVM languages.

Just like F# will always be a shadow of C#, never getting to play with all toys, with C# slowly picking up all features that matter.

Runtimes are different from languages. The C# language has never been rebooted and is fully backwards compatible, and there's no better example of long-term support than Microsoft. You can still run apps from the MSDOS era, and even upgrade MSDOS through to Windows 10 if you have all the CDs today.
Runtimes and languages go hand-in-hand, a runtime that doesn't support everything that one expects from the standard library breaks compatibility.

Code starts getting full of #ifdefs

Siverlight, .NET Core, WinRT, UWP just to give three reboots.

No support for dynamic APIs, appdomains, IL generation on the fly, reflection APIs done in a different way, ...

Actually C# is not fully backwards compatible, variables declared on foreach statements changed their semantics in C# 5.

https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/clos...

MS-DOS is only supported in 32 bit variants, a species in extinsion.

> The C# language has never been rebooted and is fully backwards compatible

This is not true - there have been several breaking changes in C#-the-language since 1.0. For example:

https://davefancher.com/2012/11/03/c-5-0-breaking-changes/

So, like DOS 3.3 to 5 to 6.2, and Windows 3 to 3.1, to 95 to 98 to 98SE, to ME, to XP, to 7, to 8 to 8.1, to 10?

I think I was using floppies until Win 98?

Here's a 10 hour video from DOS to Win10: https://www.youtube.com/watch?v=l60HHWWo9z4
Huh. I didn't know they worked any differently in C#. In what ways are they less powerful in Java?
For example, in Java, lambdas cannot capture mutable variables from the outer scope - it must be final, or effectively final. C# always let you do that, from the very first implementation of lambdas.
C# lets you do that because C# doesn't have a way to declare a local readonly/final variable at all. I significantly prefer features that encourage the use of `final` variables everywhere that it is possible in Java.

I write C#, Java, and Kotlin in roughly equal measure. Each has its pluses. But the claim that Java's lambdas are worse because it doesn't let you--and this was a conscious design choice!--do something so potentially catastrophic and difficult to debug is an odd one.

C# could do the "effective final" rule, same as Java did - don't allow a variable to be closed over if it's mutated anywhere.

And yet it didn't - which made implementation that much more complex, since capturing mutable locals requires lifting them to extend lifetime.

I rather be able to do something than be prevented because someone somewhere thinks that it's "potentially catastrophic". That seems especially hyperbolic in this case.
To each their own, but I can second that most Java programmers find that ability horrifying and if it came to a vote would probably get it ejected.
Do Java programmers find mutable variables in general horrifying? That was not my impression - certainly, the language doesn't make it easy to make everything immutable. If so, why the special distinction for lambdas?

The official rationale was that they expected lambdas to be mostly used for parallel sequence processing, and wanted to avoid race conditions. Of course, in practice, lambdas are very useful in many other places, where there's no concurrency issue at all - async continuation callbacks, for example, or pseudo-custom language constructs implemented as functions with a lambda for a body.

What’s the memory model for captured variables being mutated by multiple threads?
The same memory model as any other shared variable being mutated by multiple threads - you either explicitly synchronize, or you just don't do it. But given that lambdas are far from the only way to get there - and those other ways are already idiomatic in the language (e.g. statics) - why single out lambdas specifically for this?

The ideal solution is to make this a part of the function type - so that APIs that do intend to invoke lambdas concurrently can mark them as such, and then the language would enforce sharing, while other APIs that do not use lambdas in a concurrent context, can use their full power.

Same thing goes for lambdas that cannot escape vs those that can - if you reflect this in the type system, then you can also support safe nonlocal breaks and returns in the former, for example. One of the early Java lambda proposals, the one by Neal Gafter, did just that, and it was awesome.

> why single out lambdas specifically for this?

Because existing memory models have rules for object fields and rules for publishing objects.

These rules don’t apply to local variables.

They could do - but that’s what I’m asking.

Yea, that seems frightening. I like to think of my local variables as my inaccessible local state. I'd imagine it keeps my JIT happy too. What would it even mean to have a lambda reference to a variable who's stack frame has already popped?
> What would it even mean to have a lambda reference to a variable who's stack frame has already popped?

That bit is simple - the variable is kept alive as long as there is still a lambda that references it.

Don’t think about stacks - that’s an implementation detail and the compiler is free to use a combination of the stack and the heap to implement local variables.

The issue I’m talking about is if one thread writes a local and another reads it, what does that look like?

Yeah, or value types. Still waiting on those...
You can use the extensions in IBM J9 or Azul, just like some use GCC C while claiming it is C. :)

Also looking forward to them and disappointed they weren't there since the beginning.