https://mail.openjdk.org/pipermail/loom-dev/2023-September/0...
So they could also just choose to ignore this problem for now.
https://mail.openjdk.org/pipermail/loom-dev/2023-September/0...
So they could also just choose to ignore this problem for now.
Without being an expert on the core.async internals, I believe core.async can potentially benefit tremendously from this, by the virtue of being a powerful and super-elegant API for communicating between different parts of an application (typically within the same JVM process). Now it can continue to do that, while (likely) being free from most macro gotchas...
Many of the core concurrency constructs in Clojure has separate functions for when you are doing something that is blocking vs not blocking. If it were all virtual threads this distinction generally wouldn't matter.
I can't remember the last time such a transparent, drop-in-able change was so impactful for just about everything.
While it doesn't remove the need for the event/reactive/async paradigm in all cases, it does removes the need for async code just for the sake of being non-blocking.
Code can now be written much more clearly, and still be non-blocking. That's huge.
Once you have even an arguably relatively small amount (100s to 1000s of requests per second) it can be a game changer in terms of efficiency.
OS threads in Linux are fast and you can have a lot of them. Eg https://news.ycombinator.com/item?id=37621887
``` (run! (fn [x] (future (Thread/sleep 60000))) (range 100000)) ```
(hint: likely not...)
TLDR; Efficiency difference: you can think of virtual threads as being about 1,000 times (3 orders of magnitude) less expensive, in the general case. Exception: If you are only doing CPU-only work, regular threads will be better (that's not how most web servers/services operate).
But if you're waiting 100ms of ms for your database (or any network) to respond and you have many of those (blocking) method/function calls in flight... virtual threads are the way to go in terms of efficiency.
Great video explaining all of this (at timestamp, all 30 min is worth watching):
17:05 Making threads less expensive: by how much? https://www.youtube.com/watch?v=5E0LU85EnTI&t=1025s
I don't doubt virtual threads are efficient when microbenchmarked against OS threads, but there's only so much to be gained when optimizing a part of the system that wasn't a bottleneck to begin with.
(Also I hope in the scenario we have a beefy DB that can exploit the concurrency available in this many pending concurrent queries per client, and isn't just putting them in ever queue! I guess it could, maybe it has 1000 read replica servers, each with 100 attached SSDs or 100 cores serving from in-memory data)
I agree that for a low volume typical CRUD-only server that never bursts above a few dozen requests per second that's not an issue.
Messaging is one special "rare" case.
Imagine a messaging channel in Slack/Discord/Telegram/WhatsApp-type app, where there's thousands of participants (say 10,000) in one channel. 1 person posts a message...
9,999 messages are generated (one to for each of the other participants). One might say, that can be handled with a few machines...
Now imagine a person posts something super interesting to the channel where 100 people respond with an emoji reaction almost instantly.
Now you have 100 * 9999 = 999,900 (!) messages, all within a few seconds! And this is for just one message _reaction_. Each one of those messages likely involves multiple steps of IO (sending/receiving data over the socket, writing to one or multiple databases...). So a thread might be blocked for hundreds of milliseconds at various times, which would ultimately likely require 100-1000x more servers to operate as scale increases.
The thing is that you _can_ write a program efficiently using regular threads by using asynchronous techniques. Another name for such style of programming is _callback hell_ :) . And when something does go wrong, it is a lot harder to debug.
Now Java is moving fast and they are taking JVM compiler, runtime, class metadata etc along where Java, the language is headed.
Since most platforms are leaky, one ends up needing to master two ecosystem in parallel, eventually it gets tiring and always cost extra in development.
C++, Objective-C and Typescript get around this problem in UNIX and Web, as they are extensions of the platform languages, not something completely different.
Java programmers will have only a small learning curve before feeling productive, and can gradually get more comfortable with idomatic Kotlin as they progress through their journey.
Kotlin, as a language, was very well through out, letting you use as much or as little of it as you are comfortable with.
I don't see any value in Kotlin outside Android shops.
Developer experience is important to some people - not just devs, but those who seek to recruit and retain good ones.
Of course, if you're looking to hire massive numbers of butts-in-seats (gov work, large enterprise shops, etc) I suppose Java is probably the ideal choice.
Kotlin libraries are interoperable with Java and vice versa. In some specific cases, a Kotlin library needs a little syntactic sugar to make it work with Java like a Java developer would expect, but it's easy to do. Going the other direction though - Java library used in Kotlin, zero issues, it just works. Some Java developers don't even realize they're already using Kotlin - take a look at OKHttp and friends...
Lastly, there's Kotlin support is all major IDE's, including IntelliJ of course, but also Eclipse and likely whatever editor/ide you prefer. Kotlin is basically just a bunch of libs.
I would argue Kotlin is better outside of Android than in Android. For backend work, particularly when coupled with Spring/Boot, it's freaking amazing. The Spring team has put in an impressive amount of work to make Kotlin a first-class language within the ecosystem, and it really shows.
Telling me there is Kotlin support in Eclipse only tells me you never actually tried it, specially after JetBrains ramped down the team that was doing the now stale plugin.
Coroutines are a specific paradigm within Kotlin, and should not be in library code anyway. With the addition of JVM Virtual Threads, the need for Coroutines is significantly diminished anyway.
It's anecdata, yes, but I've written a lot of Kotlin code and do not use coroutines. Kotlin really just lets you do what you want - as much or as little idomatic code as you need.
Kotlin on the backend is amazing. You really should give it a try. Java sucks badly, in comparison (I say that as a former Java nerd...)
[1] https://marketplace.eclipse.org/content/kotlin-plugin-eclips...
After installation of the Kotlin plugin the Java plugin is not running anymore. I cannot open a Java project anymore.
The log file says:
!MESSAGE org/eclipse/jdt/internal/ui/refactoring/actions/RenameJavaElementAction$AjcClosure1
!STACK 0
java.lang.NoClassDefFoundError: org/eclipse/jdt/internal/ui/refactoring/actions/RenameJavaElementAction$AjcClosure1
at org.eclipse.jdt.ui.actions.RenameAction.<init>(RenameAction.java:60)
at org.eclipse.jdt.ui.actions.RefactorActionGroup.<init>(RefactorActionGroup.java:372)
at org.eclipse.jdt.ui.actions.RefactorActionGroup.<init>(RefactorActionGroup.java:206)
at org.eclipse.jdt.internal.ui.packageview.PackageExplorerActionGroup.<init>(PackageExplorerActionGroup.java:139)
at ...The beauty of Eclipse is the ecosystem. If that official Jetbrains plugin doesn't do it for you for some reason, then use another one[1].
[1] https://marketplace.eclipse.org/content/enhanced-kotlin-ecli...
Prove me wrong.
There's nothing special about Kotlin. It's a bunch of libraries for runtime and compile time... like anything else you use.
There is a difference between supporting a language with a Notepad++ like experience, and what JetBrains was doing before ramping down the Eclipse plugin team, as means to sell more InteliJ licenses instead.
It compiles fine with Maven or Gradle, it provides auto-prompt/complete/hints. It just works.
And above all, no crashes left and right.
Kotlin is plugins for Maven/Gradle, and classpath libraries. There is no magic going on here...
All that to say, I believe that Kotlin has a lot of value, independently of Android.
I say this as a Clojure fan, there are many instances where I'd probably choose Kotlin nowadays. You can also mix JVM languages within the same application; I've found it rather useful to sprinkle some Clojure into a Java/Spring application for certain tasks. Maven and Gradle both have tooling to handle it :)
Unlike C++ though, one day the powers that be will just remove features as part of yet-another framework rewrite and you'll just have to deal with it.
The other issue is just how tightly coupled C# as a language is to the .NET framework/platform. Some of the things people like C# for are actually framework things and they don't realize it because that separation isn't as clear as with Java and say, JavaEE or Spring or $X.
If you talk to a C# developer - of course they're using .NET and of course they're using Entity Framework - to them, there's no other way. It's inconceivable that any other library or framework might do it better than Microsoft's official offering... and then you'll find the official offering to be lacking in fundamental ways.
Talk with a Java developer and you'll find there's a lot more diversity of frameworks and libraries being used - so there's a lot more differing opinions on priorities and a lot more good ideas being tried out or implemented everywhere. The communities are vastly different in that regard.
There is a "roles" proposal and investigation team. This roles proposal is going to unlock so much potential it is bonkers.
It's yet another double down on object initializers instead of writing constructors and factories, and unlike using a factory you have to fully express the result type of the collection instead of having it inferred (they are "target typed", another feature that probably should not have been added).
I've read the proposal, design meeting notes, and GH conversations. Not seeing a compelling counter argument here.
Like what?
I'll give you nullable types, though I personally don't struggle with null at all.
I think Kotlin is at risk, though, of having the JVM force them to support two competing models to accomplish the same thing - coroutines and virtual threads is a good example (although Java still doesn’t have language level structured concurrency). This could really make things messy.
I just don't think any feature of the language of Kotlin is that much better than Java's implementation. I'm also a big supporter of brevity != clarity when it comes to code.
What language feature in Kotlin do you think is that much better?
It's weird being on the other side of the fence now - hearing the same things I used to say.
Kotlin was a gateway drug out of not just Java, but OO/Solid and into FP for me (FP being another thing I used to not "get"). The beauty of Kotlin is you don't have to ever write FP code if you don't want to - but you will want to before too long.
I would recommend trying it out for yourself. It's Spring integration is amazing, and you'll have a great time. Write even a little Kotlin and you'll have an "ah ha!" moment and everything will forever be different.
It's one of those things where you need to experience it for yourself before you actually understand how great it is. There is no argument you will hear that will convince you, sadly. I was the same once...
- Having to remember the difference between "Function", "Consumer", "Predicate" and "Supplier" is weird. In Kotlin (and basically every other typed FP language that I know), I just write the type signature the natural way.
- Having to use streams in order to use map, etc., is noisy. Why weren't these APIs retrofitted to collections?
- Checked exceptions are a pain with functional code, e.g. map.
- Mutability is still the default behaviour of almost everything in Java, save records (and strings).
etc.
Sure, methods along the lines of the following could have been added:
public <R> List<R> map(Function<? super E, ? extends R> mapper) {
return stream().map(mapper).toList();
}
Yes, having them would make Java less verbose in the case where you only want to do a single map, filter, whatever operation on a collection, but I'm personally glad that they weren't because they produce extremely inefficient behavior when chained. So, it would add a performance footgun to save ~18 characters.- I don’t think this is really that big of a deal.
- Using streams is actually preferred so you don’t cause a performance nightmare and kotlin you has to use asSequence anyway.
- I thought checked error handling was all the rage nowadays?
- Mutability is absolutely fine if you’re not going over a thread boundary.
And checked exceptions are widely seen as an anti-feature - I don't think any other language has them, with the possible exception of Swift (see below), and for good reason. Other languages encode errors (more or less ergonomically) in the type system (e.g. Rust, Haskell, and to an extent it's what Kotlin advocates as well), and that has the advantage of playing well together with other language mechanisms - e.g. no extra allowances need to be made so that map works with them.
But for the sake of the argument: Swift also has something similar to checked exceptions, but only in the sense that functions can either throw or not throw. However, Swift also at least has constructs like "rethrows", which lets you say that a function such as map should throw iff the higher order function it wraps throws.
Java simply decided that it doesn't need to deal with this problem at all (for whatever reason - they simply could have added something like "rethrows") and now it's up to callers to deal with the incredible awkwardness that is using FP with Java library code that makes heavy use of checked exceptions.
var, records, sealed types, and pattern matching make a huge difference in how Java is written. A lot has changed since 8.
I left the Java world for the C# world around 2008 and then returned to the Java world last year. C#/.NET changed much more significantly much faster. Almost all of the significant differences in day to day life with respect to the Java language and standard libraries that occurred between 2008 and 2022 are due to changes introduced with Java 8.
I don't think it's that good an example, though there's some merit to it. Kotlin coroutines, to me, target three main use cases:
1. An abstraction over an underlying threading model. Get threaded execution without having to explicitly deal with thread pools and such. I'd consider structured concurrency to be part of this use case.
2. A way to write non-blocking code that is structured as if it were blocking. (Incidentally: I love Kotlin's decision to make "await" opt-out instead of opt-in.)
3. A way to write code that generates sequences of values without having to either write an explicit class that maintains state across calls or use callbacks/CPS. Or in other words, a way to use the "yield" function.
Use case 1 still makes sense with virtual threads. Run your coroutines on an executor that uses a virtual thread pool instead of a platform thread pool. If you prefer the coroutines API, you can still use it for structured concurrency and such.
Use case 2 is much less interesting in the presence of virtual threads; you can just write blocking code directly, no need for the language to turn it into async code for you.
Use case 3 has nothing to do with threads to begin with, so it remains exactly as valuable as before and there's no reason to change any existing code at all.
Do coroutines get less useful in the presence of virtual threads? Absolutely. But they aren't competing with virtual threads; there are substantial non-overlapping use cases that make them a worthwhile language feature.
Tying down a value to a name only if you have something meaningful to say in the name, and only when the value is actually finished instead of being a work in progress, that gives me great piece of mind and frees up mental resources for other things. I wish every language had a "kotlinized" sibling that simply added the scope functions on syntax level (or a subset of the scope functions, not sure you need a zoo that size)
From my perspective it is terrible enough that the language specific wrapper library has 10x to 100x the usability of the Java option if you use it directly from Java. For example, GORM for Groovy is significantly better than Hibernate even though it is built on top of Hibernate. It is so good that I chose to use groovy once, not because it made sense but simply because Hibernate is so terrible, the cost of a different language was outweighed by it.
I don't give a damn about most JVM features. They don't change the crap ecosystem at all. I personally despise the JVM for being incredibly memory inefficient as well. None of this is going to change.
All the power to people who enjoy working in Java, but that doesn't have to make me like it.
I would also like to mention, for non-Clojure users: it took ages for Clojure to finally get off Java 1.5. The compiler was still emitting 1.5 compatible bytecode as late as 2018. Since the release of Clojure 1.10 (17 December 2018), the minimum Java version was bumped up to 1.8, in the sense that the compiler now emits 1.8-compatible bytecode.
There is a strong emphasis on moving slowly and NOT breaking things in the Clojure community. Likewise, if you look at the most recent Clojure survey results, the vast majority of people were still on 1.8 and 11 releases.[1]
[1] See Q24 in https://www.surveymonkey.com/stories/SM-_2BH3b49f_2FXEkUlrb_...
I'm not making a judgement on which decision is better, but I can totally see the appeal, at least in the short term, for people choosing Clojure, especially if they think that Clojure's API will be largely unchanged in the Java 8 update. It doesn't hurt that Clojure is just broadly a really pleasant to work with, at least in my opinion.
You are in deep deep trouble already, just process-wise. If you're making money hand over fist you might be able to afford to do that, but choice of language is not even on the radar in terms of what needs fixing.
Now, if you mean in terms of digging out of that hole... just upgrade to newer dependencies, newer JDK versions, etc. That's the way to better performance, better memory usage, better GC, better everything... and THEN think about the language you're using.
Code is usually a much smaller piece of company's business process than we developers like to think.
> If you're making money hand over fist you might be able to afford to do that
Here's the problem though: the vast majority of companies on the planet don't "make money hand over fist", they just have small to reasonable profit margins. And most businesses can't just risk losing what's already been working for a decade just because some developers want a new shiny thing every six months.
That's the reality which JVM and Clojure folks take very seriously, but is very often overlooked by younger people.
I agree.
Btw, I think you inverted the meaning of my sentence regarding 'hand over fist'. I was saying that staying on an old version is asking for trouble and is going to cost a lot more than upgrading.
> That's the reality which JVM and Clojure folks take very seriously, but is very often overlooked by younger people.
Ah, the irony of being called young because of a comment I made :). I'm an old hand at this point.
FWIW, my shop also stayed on JDK 8 for a bit longer than we should have, but that was largely because of upstream dependencies. We're currently on JDK11, but again just because there are some upstream dependencies that are holding us back from JDK17. (It's mostly to do with the infamous module system change. It's a good change overall, but some projects have been super-slow to adapt.)