Kotlin is great and if you want a cleaner Java experience, by all means use it but Scala is real champion in pushing many, many things into the mainstream.
I disagree.
Java records are from Standard ML but sealed is not, it's from Scala with the improvement that subtypes of a sealed types are sealed too.
Java still not have a real syntax for ADT, Standard ML has datatype, Rust or Scala 3 have enums on steroid [1].
sealed interface Expr {}
record ConstantExpr(int i) implements Expr {}
record PlusExpr(Expr a, Expr b) implements Expr {}
record TimesExpr(Expr a, Expr b) implements Expr {}
record NegExpr(Expr e) implements Expr {}
For non-record classes, deconstructing patterns will be something like a dual to methods, and almost a first-class citizen. Among other things, they will allow patterns to support API designs that predate records and sealed types.Java misses the syntactic sugar to define ADT in one place like in Standard ML, OCaml or F#.
The closest you get is to declare records inside a sealed interface, but once you add generics in the mix, it starts to get messy.
I was not able to define a classical list with cons and nil in Java.
sealed interface List<T> {
record Cons<T>(T car, List<T> cdr) implements List<T> {}
record Nil() implements List {}
}
The compiler give you warnings, Cons<T> or Nil are not a top level type and you can create several instances of Nil.I don't think so.
> I was not able to define a classical list with cons and nil in Java.
If you want to restrict Nil to a single instance, don't make it a record class:
sealed interface List<T> {
static <T> List<T> cons(T car, List<? extends T> cdr) { return new Cons<T>(car, cdr); }
static <T> List<T> nil() { return (List<T>)Nil.INSTANCE; }
record Cons<T>(T car, List<? extends T> cdr) implements List<T> {}
enum Nil implements List<Void> { INSTANCE }
}
You can statically import if you mind them not being "top level": import static List.*;
And then write, say: var x = cons("a", nil());
var y = cons(1, x);
Not as succinct as ML, but workable.Although, in this particular case,
record List<T>(T car, List<? extends T> cdr){}
would suffice. List<String> list = cons("a", nil());
switch(list) {
case Cons<String>(var car, var cdr) -> ...
case Nil -> ...
}
Nil is typed List<Void> which is not compatible with List<String>.You may be able to use extractors as the inverse of the static methods cons() and nil() but that adds more boilerplate.
Disjunctive enums like in Rust or Scala 3 is a simpler way to define ADT.
Of course, Java could add rules for the special case of zero-argument records, making them more enum-like, but I really think the value in doing that is negative. Other than being able to directly translate ML examples, it would add complexity that isn't worth it.
In fact, in this particular case I'd do it like so:
record List<T>(T car, List<? extends T> cdr) {
public List { if (car == null) throw new IllegalArgumentException("null element"); }
}
Which would give you: switch(list) {
case List<String>(var car, var cdr) -> ...
case null -> ...
}
It might not be exactly what people who like Scala might prefer, but it is very much a good representation of ADTs for those who prefer Java.Sealed classes, data classes, multiline strings, coroutines -> project loom, switch statement improvements to resemble Kotlin when statement, improvements to Streams to match Sequences.
That's not accurate.
Popularity of a feature and enterprise demand drive it's adoption.
It doesn't really matter what the language team knows, it matters what the average developer knows since their collective wants are in large part what drives the language team.
There are hundreds of thousands of Java devs who only ever wrote Java and suddenly had to write Kotlin, and are now clambering for these feature improvements in Java.
2. Java addresses those topics (where relevant) pretty much in the opposite way from Kotlin. Java rejected coroutines and is going down a completely different path with virtual threads; Java rejected data classes and chose to go down algebraic data types. So if you think Java gets inspiration from Kotlin, then it is in the form of what not to do.
If anything, the acceptance of more parts of the FP paradigm into all major languages (like C#, JS/TS, C++) starting with lambdas likely have pushed Java, but to single out the all-around very niche Kotlin language as a source is imo a non sequitur.
It's also important to not be US centric. In many parts of the world Android is the dominant OS.
I really struggle to see Kotlin as being part of the debate here. These features are being driven by enterprise Java user pain points. And those people aren’t looking at Kotlin.
By Java devs who only ever wrote Java, and suddenly had to write Kotlin for Android. They're now aware of these features as a result and are demanding them in Java.
I guess it only has decent marketshare because of google's android support(they are throwing stuff on the wall to move away from Oracle IP i guess) and Jetbrains being very popular among java devs.
The sour grapes of the Kotlin/Android marriage is that going forward one will need KMM for code between JVM and ART, or be happy to just use what ART undestands.
1) Jetpack Compose is very loudly Kotlin-first
2) I did a survey of Android developer job postings. Literally ALL of them were for Kotlin, with Java mentioned as a nice-to-have roughly 50% of the time.
On a technical note, I don't see Java fixing NPEs, checked exceptions, or general verbosity any time soon. Java gets to live with its legacy of bad choices, Kotlin is able to stay clean while still providing backward support by integrating with old Java code .
What did the trick are the other programming languages that can stand on their own as platforms.
Edit: see pron's comment below.
If you look at most of the recent additions to Java, they were in Kotlin from the beginning. This is no coincidence.
Kotlin absolutely nailed the "meaningfully better than Java while staying pedantically true to the existing semantics" in a way that none of the alternatives did even if they did come first.
Are there stuff that kotlin's pattern-matching can't do?
> The primary issue I can think of comes from the lack of pattern matching. Kotlin’s language designer left out pattern matching intentionally because it is a complex feature whose use case is primarily for building compilers. However, modern Spark (post Catalyst / Tungsten) look a lot like compilers and as a result the internals would become more verbose if built using a language that doesn't support pattern matching.
I like to refer to it as Haskell with an extra chromosome: slow, hard to comprehend, and incredibly strong.
I'm lucky enough to write it in my day job and I feel like I'm living life on easy mode because of it. It'd take a lot of money to lure me back to garbage like Python/JS at this point.
that's a weird and offensive thing to say
That's why some call it Haskellator. :)
This also means that many codebases have different styles though, whereas in Haskell you have one style (FP).
The primary problem with threads isn't their performance, but the way that they encourage bugs and race conditions by decoupling data access from flow control.
They're fine as a low-level API for implementing a more usable API, but not as the primary API that you expect application developers to use.
And Loom will be absolutely killer feature, it’s not about performance, it’s about blocking. Abstraction built on top of them will be easier to reason about, no callback hell, etc.
As I already wrote? But doing M:N scheduling at the JVM level (which is ultimately what Loom is) won't help your future (/promise) runtime which already implements a thread pool internally.
> And Loom will be absolutely killer feature, it’s not about performance, it’s about blocking.
If you like the blocking model and don't care about performance then you can go and use native OS threads in the same way right now.
M:N scheduling (including Loom) is ultimately about making threads cheaper to create, which means that theoretically you can have the "best" of both worlds (performance of futures, API of threads). My point is that this is a nonsensical goal, because the API of threads is awful.
> Abstraction built on top of them will be easier to reason about, no callback hell, etc.
Until you try to introduce concurrency, at which point you're back to the hellscape that is manual multithreading.
https://www.reddit.com/r/haskell/comments/ncaud3/scala_has_d...