Kotlin vs. Java
kotlinvsjava.com
kotlinvsjava.com
Java 14 is getting an experimental preview of Records (https://openjdk.java.net/jeps/359) which takes care of much (but not all) of ceremony around "data classes".
And Java 13 got a preview of text blocks, https://openjdk.java.net/jeps/355.
Things are coming. That said, I hardly believe those minor syntax improvements are what is so "good" about Kotlin. It has some better defaults (non-null by default, final classes by default), and is a more modern language. Java will stay with us for many years to come, though.
But some Java SDK exceptions drive me crazy. For example `URL.parse("http://example.com")` over hard-coded strings that I know 100% won't throw exceptions, but I still to catch 3 of them.
https://docs.oracle.com/javase/7/docs/api/java/net/URI.html#...
After URI appeared in 1.4, the only reason to use URL was to create a URLConnection from a URI. Since openConnection() throws IOException, it's not a big deal that toURL() throws a MalformedURLException - just catch it along with all the other IOExceptions.
Since 11, there's no reason to use URL at all, because you can use HttpClient to actually do HTTP.
The JDK is filled with a ton of dumb "once upon a time we thought this was okay..." things that don't properly encode the correct modern idiom
Newer languages definitely have an advantage here, in that they haven't been around long enough for people to have figured out which bits of them are dumb.
Would probably have gone unoticed at most AWS/GCP/Azure shops today.
At that point, URLs resolving to the same IP were considered equal, even if the host names were different. Even now, there’s no real difference between say “http://example.com” and “http://example.com:80”; it would have been reasonable to consider these equal.
google.com and maps.google.com resolve to the same address but are not equivalent. Even if your example and many others are equivalent, there are many that aren't. A general library should not make assumptions like that, unless they hold 100% of the time.
The spec I am mostly familiar with is, however, rather new, and whatever standard controlled URLs at the time (if any) might have made more assumptions.
Helluva way to thread starve your application. :(
https://docs.oracle.com/javase/7/docs/api/java/net/URL.html#...
How would you implement them?
One example of this: there is no way to encode the type of a checked exception in a generic. I would like to be able to express something like the following:
interface ExceptionHandler<E extends Exception, S, T> {
T wrap(
Function<S, T, throwing E> fn,
Function<E, T> exceptionMapper
);
}
But there is no way to express that 'throwing E' part. The type of a checked exception is firmly embedded in the interface. So I can't supply, say, an IO method as a Function<S, T> parameter, since the IOException causes a mismatch, and I'd have to write a handler specifically for methods that throw IOException, another for those that throw TimeoutException, a third for those that throw IOException AND TimeoutException, etc; a fairly fruitless goal without automatic code generation. interface FunctionThrows1<S, T, E1 extends Throwable> {
T apply(S in) throws E1
}
interface FunctionThrows2<S, T, E1 extends Throwable, E2 extends Throwable> {
T apply(S in) throws E1, E2
}
...
But you still have to write one version of your method for each arity of throwing that you want to be able to wrap.That's partly because there's another problem. Potentially, you could have streams of the form, say, `...map(A::foo).map(A::bar).map(A::baz).collect(...)`, and each of foo, bar, baz adds another exception type to the set that can be thrown by collect.
In practice, you would end up with the stream throwing an exception of some general type (in 95% of cases, IOException!), but you could still write specific catch blocks for each possible subtype.
I wish Kotlin had, instead of ignoring the existence of checked exceptions, instead translated them into part of the return type. I use Kotlin a lot these days, and one annoyance for me is dealing with code that throws exceptions. They fixed the annoying "(almost) anything can be null" problem of java and replaced it with an equivalent problem. Why can't nullability and failure results both be part of the static type?
(The workaround it to manually use an Either type yourself, but it doesn't help you with calling anyone else's code, since virtually everything throws exceptions on failure.)
How do you declare a method that generically takes a function that in turn takes a K, returns a V, and can throw whatever checked exceptions it wants, and you'll rethrow them? Last I checked, this wasn't possible in Java.
If checked exceptions were instead replaced with sum type return values, then it becomes trivial.
@FunctionalInterface
interface ThrowingFunction<T, R, E extends Exception> {
R apply(T argument) throws E;
}
static <T, R, E extends Exception> R apply(T argument, ThrowingFunction<T, R, E> function) throws E {
return function.apply(argument);
}
But not catch and rethrow it, because throw and catch aren't generic, and the type parameter isn't reified. You can emulate reified generics with the usual trick of passing a Class object: static <T, R, E extends Exception> R apply(T argument, ThrowingFunction<T, R, E> function, Class<E> witness) throws E {
try {
return function.apply(argument);
} catch (Exception e) {
throw witness.cast(e);
}
}
But this is pretty horrid.This thread stemmed from https://news.ycombinator.com/item?id=21808522 :
> Java's implementation of checked exceptions looks pretty minimal and sound to me. > > How would you implement them?
Java's checked exceptions aren't "minimal" and are problematic because they behave unlike the rest of the types system. I'm not saying that they should have ditched checked exceptions and left it at that, though. What they should have done was make sum types a first-class part of the type system, and then checked exceptions become redundant.
In languages that actually have general sum types rather than special-casing the way Java's exceptions do, you can get multiple types of errors with an Either by making a union of the different types of errors. You can alternatively use an N-way sum type as your return value in place of the 2-way Either.
Kotlin has sealed classes, which can be used to create a sum type, and you can even do the same in Java with enough boilerplate, but in either of those languages you run into another problem if you try this: it doesn't interoperate well with the vast majority of existing library code that throws exceptions.
1) They follow a different code path (which is what makes them a superior way to handle errors over return values).
2) They cannot be emulated by sum types when all you want to do is not handle the exception and let it bubble up the stack frames.
It also allows the exceptional behavior to be defined and to be controlled by the developer, one thing that you don't really have today when throwing RuntimeExceptions with say Stream APIs.
Checked exceptions were an excellent hack, but an ergonomic failure, despite the almost-pattern-matching of the `catch` clauses.
in reality though aside from the rock/hardplace of language conservatism/fanatical back-compat i have to imagine a real reason modern java has yet to fully embrace Optional/Either over null/exceptions is lack of value types - yeah escape analysis lets you ~mostly~ not have to worry about the IdentityObjects you're returning everywhere, until you hit something it can't/won't inline or try to stuff those Optionals on heap and wind up making our already painful pointer chasing situation ten times worse. value types are also well on their way via project valhalla but have yet to actually land.
maybe ironically one of the things i recently got bitten by was the fact that MethodHandle .invoke* methods throw Throwable when used directly in plain java - the very machinery that enables the efficient composition of these kinds of functional programming styles is gated behind having to catch literally anything in the language itself. i guess the recurring theme here is java putting the horse before the cart, and that's frankly my favorite thing about the ecosystem.
Either/Result has often been found to be untenable without these mechanisms, as well. Rust first added the try! macro and then the ? operator because it was so tedious to deal with raw Results otherwise.
> IOException.
Rather than which alternative? There are tonnes of IO errors that can occur and are captured in Java that you may not ever even consider. IO is exceptionally tough and programmers should be aware that there could be 1 of a possible million reasons for failure, even if they don't care exactly why.
It's not a massive ask either:
try{
/* Perform some IO that 99.999999% of the time */
}catch(Exception e){
/* Something went wrong and we don't know the state of the disk */
}
Personally I believe applications should have their own wrapper around IO and handle it accordingly. I.e. not caring, re-trying, show-stopper, etc, etc.A lot of code I've written will have the following if I really don't care if it works or not:
try{
/* IO code */
}catch(Exception e){
/* Do nothing */ // <-- Let others know that this was on purpose
}That said, even if typed checked exception handling is important...it quickly becomes untenable.
Thing thing = create();
try {
...
} finally {
cleanup(thing);
}
You might try to abstract this withThing(thing -> ...)
But what if "..." potentially throws IOException? What if it potentially throws IOException or AWTException?You have to give up all your exception type safety to make an abstraction like withThing. It's just not composable.
interface ThrowingConsumer<T, E extends Exception> {
void accept(T value) throws E;
}
// ...
<E extends Exception> void withThing(ThrowingConsumer<Thing,E> callback) throws E { ... }
The problems are that (a) you need to add an extra type parameter for exceptions all over the place, (b) exception unification (sum type) doesn't work generically - you can say E1 | E2 in a catch block but it's not a real type, and (c) it composes poorly with existing libraries that expect Consumer<T> throughout.For one level deep callbacks (for resource handling and the like) it works reasonably well though.
try {
...
} except (IOException|NoSuchElementException e) {
throw new RuntimeException(e);
}
If you decide you don't want checked exceptions in your code, wrapping the exceptions at the boundaries is not a huge deal.Java was designed for "production" software, which in the late 90s and early 00s meant software that ran for long periods of time as a server, servicing thousands of mission-critical customers. Crashing was not acceptable behavior for that. But now a lot of production software often requires a lot of ancillary one-off tasks that are just done by the developers - testing, data-munging, exploratory code, migrations, demos, etc. That's a very different environment from where you code to spec, the spec never changes, and once the software is done it's supposed to run for years without crashing.
And if you also consider the upgrade treadmill, and where the average Java developer is relative to say ten years ago, it may turn out that at any given point in time, the Kotlin version you'd be able to deploy may have a number of features the most recent Java version you could deploy does not have.
(to say nothing of companies where 'the version' is decided by committee or fiat and thus having a more obscure language sometimes gets you less scrutiny).
Even considering ART as the KVM, isn't a safe deal, as it is written in a mix of Android Java and C++.
And currently its potential is very tiny when measuring all JVM guest languages against Java, using any recent language report.
There is nothing to fear from Kotlin, it is a guest language on the JVM.
It is only relevant on Android, as Google is making ART into the KVM.
Outside Android, Kotlin market share adoption is very low, as any trends chart will easily prove.
The thing I found hard at times with Kotlin is that it allows/relies on Java classes/frameworks but almost all of those classes/frameworks have their documentation (and only resources) written in Java so you have to have that additional mental step of translation between the two languages.
If you are not competent with basic Java, you may struggle to utilize those Java classes in Kotlin effectively.
Finally, when it comes to hiring people/finding job for Kotlin development, outside of Android jobs, there are very few positions requiring Kotlin which in terms means that there are very few non-mobile developers working with Kotlin as compared to Java. Now this may not be that much of a concern to us Developers, but for Management and leadership, it can raise a big concern towards adopting a language which from perspective of labor market comes across as a niche.
Having said all this though, I have no doubt at least in Android development, Kotlin will replace Java as programming language of choice but I don't think it will find traction beyond mobile (to displace / challenge Java).
I'm rather unfamiliar with Kotlin as a language. Is Kotlin for Android rather idiosyncratic as is Android Java? In other words, is there a superset of language features in Kotlin that goes beyond Android and is more expressive than modern Java?
It has lots of language features that make it more expressive than Java (try doing constructor dependency injection in both!) but these are not specific to Android.
Firstly, quite a few large shops have started to use it, but usually due to grassroots bottoms-up activity of the sort that takes a while to percolate through to recruiting departments in distant departments where changing a job req is a heavy process.
Secondly, there's really not much incentive to try and change job reqs like that because it would be extremely hard to advertise via job reqs We use Kotlin but you don't have to know it to apply. To a non-technical recruiter or HR department this looks like a useless statement and will frequently get "fixed" to what you "really meant", something like "Java experience is required, Kotlin highly desirable" which might just put off candidates who don't know what Kotlin is or how easy it is to learn. You're just restricting your pool of candidates for no reason in a highly competitive market.
Much better to advertise the usage of Kotlin in places where developers looking for new opportunities might notice and leave the job reqs alone, and let Java devs get up to speed in the first few days. For example:
https://medium.com/ing-blog/introducing-kotlin-at-ing-a-long...
Edit: To answer your later question, yes, Kotlin goes beyond Java in a lot of ways other than nicer syntax. For instance it has a different (better) generics model, it has far more type inference, it has sealed (sum) types, it has language-integrated coroutines, it has compiler plugins that automate, amongst other things, serialization, it has delegated properties and interfaces, it has type aliases, it has extension functions etc.
I suppose a lot of these can be considered "just" better syntax than Java (except for coroutines and generics), but, at the heart of most programming languages is making patterns that are possible in other languages much easier.
I'd say that sentiment is lazy. There are lots of reasons why someone wouldn't or couldn't spend time learning Kotlin (kids, other family, other hobbies, list could go on).
This code:
if (nullableVariable != null) {
boolean success = nullableVariable.someMethodCall()
if (success) {
return success
} else {
return fallbackIfNullMethodCall()
}
} else {
return fallbackIfNullMethodCall()
}
Could also be written something like this: result = Optional.ofNullable(nullableVariable)
.map(NullableType::someMethodCall)
.orElse(fallbackIfNullMethodCall());
Sure, it's no Elvis operator, but it's a lot more readable than the "standard" if/else structure of traditional Java. If we're comparing Java and Kotlin, we should at least give Java a fair chance. I dislike the verbosity of the Optional wrapper, but it's a lot better than if/else checking. boolean success = false;
if (nullableVariable != null) {
success = nullableVariable.someMethodCall();
}
if (success) {
return success;
} else {
return fallbackIfNullMethodCall();
} if (nullableVariable != null) {
boolean success = nullableVariable.someMethodCall()
if (success) {
return success;
}
}
return fallbackIfNullMethodCall();
And even that can be more concise if you prefer: if (nullableVariable != null && nullableVariable.someMethodCall()) {
return true;
}
return fallbackIfNullMethodCall(); result = Optional.ofNullable(nullableVariable)
.map(NullableType::someMethodCall)
.orElseGet(() -> fallbackIfNullMethodCall()); result = Optional.ofNullable(nullableVariable)
.map(NullableType::someMethodCall)
.orElseGet(this::fallbackIfNullMethodCall);Thanks for the feedback.
In Java: when `nullableVariable` is not null and someMethodCall() return `false` then we return what `fallbackIfNullMethodCall()` returns right? and in Kotlin in that case we just return `false` from `someMethodCall()` because this is not null and `fallbackIfNullMethodCall()` is not even evaluated?
(Actually that sounds like a Future<Optional<T>> or something.)
return nullableVariable != null && nullableVariable.someMethodCall() ? true : fallbackIfNullMethodCall();
So "nullableVariable != null ? nullableVariable.someMethodCall() : fallbackIfNullMethodCall();".
But I agree, in this specific case using a ternary operator after a null check is probably easier.
Kotlin code is something any Java programmer can read with just a few days learning, however.
One example was they were using enumerations and got boxed in. They had to add unit tests to ensure they enumerated all the options. Why does Scala have that feature? It's full of bad features you should avoid.
It is really hard to become a good Scala programmer, but it is not hard to become a good Kotlin programmer. All the little things that make Scala theoretically better are not worth the learning effort. In Kotlin you get 80 % for 20 % of the effort.
Scala is separated in subcultures. Some developers use it as "Haskell for the JVM" and other as a "better Java", or something between. That makes hiring and picking up existing projects hard.
Tooling is another weakness of Scala. Scala IDE (Eclipse) is more or less dead. IntelliJ with the Scala plug-in is ok, but the Kotlin support is much better.
Kotlin on the other hand is just a breath of fresh air, very easy to pick up and get productive.
I just hope they keep the language to a manageable size and don't keep putting things on top.
I'd still prefer Scala to Kotlin any day as I still think it's a stronger language. We had to use arrow library to make Kotlin more like Scala. Especially if you're leaning more functional, standard Kotlin will be lacking.
That being said, Kotlin's entry point is much much lower for Java programmers. Kotlin is a better Java, so I can understand the appeal. I'd never start a new project in Java when Kotlin is available.
Kotlin is like Clojure, or Elm, or Rust: it is lean, orthogonal, and thought out. It has been intellijently designed.
My choice is firmly behind designed languages.
Also I don't agree it is like Rust or Closure. Kotlin got majority of features from Scala, and contrary to Rust or Scala it does not offer anything innovative. Kotlin is just Java with nicer syntax. Scala and Rust are more complex, but these are the few that move the needle.
Also, the great 100% interoperability with Java makes possible to migrate part of your codebase to Kotlin and using tons of already proven and great libraries in the Java ecosystem. On top of the Kotlin standard library includes many improvements of the existing Java classes from collections to String, and all of this using extension functions, one very usefull and powerful feature.
I can keep going on and on, but to conclude Kotlin is not just Java with improved syntax, but completely new modern programming language incorporating or "borrowing" some of the best features from Scala, C# and other popular programming languages. So in my opinion it is a great language which has a lot more funtional features than Java, but still pragmatic and easier to learn than Scala.
Try to call co-routines from Java, use SAM types with default methods from Kotlin, just for starters.
They are making concurrency a first class citizen. With items like Go channels, Observable etc. This is baked into the language. There are a number of new frameworks utilizing these patterns. Resulting in less code, and more performance. At the cost of sometimes more difficult debugging.
The bigger item to me is multi platform. I'm working on a micro orm. That reflects your schema automatically generating types for NodeJs, Native, and JVM(Graal/JDK). It can then be run on any of those platforms. An example is libpq for postgres allows streaming and observing rows on update. This is not in the DB libraries that I've seen yet. I can have an actor run natively on LLVM, talk back via grpc/rsocket/tcp/etc. To a JVM/JS service. All while adhering to the same interface / method description.
Besides that. There are scripts for kubernetes, react/angular/vue, etc. Dukat is coming to automatically convert ts.d files to kotlin description files. For me I can have one language, one IDE, for every part of my product. From deployment, back end, data handling, front end, and mobile.
Moving between Channel and Flow sucks. No async/await keywords like Python, C#, Rust so you have callback hell. try-with-resources doesn't work with async callbacks (i.e. thenApply, thenCompose, etc.).
Also, JAX-WS standard doesn't currently support Channel or Publisher<ByteBuffer> for body types.
But you have to understand that many many libraries are on Java 1.8 because that's what Android supports. Even when Loom arrives, many libraries won't use it but will rather continue to use threads.
BTW parts of Loom already shipped.
On the other hand, outside of certain core libraries this won't matter much. You don't do a whole lot of scalable blocking IO on phones.
Ah that's a nice way to do it. But then what happens to Channels? Are they cast aside so we go back to InputStream which blocks the fiber?
> You don't do a whole lot of scalable blocking IO on phones.
No but you can save a lot of memory, a lot of context switches, and CPU depending on the scenario.
val foo = if(x) y else zIt is also obvious that Java version of the variable syntax is awful and just asking for unintentional modification.
There is no difference. Ternary operator is a syntactic sugar for if/else.
> Is there even any reason for if/else that doesn't return a value (that may be void/unit/similar)?
Main reason why an if statement returns a value is because it is used to test if condition is true or false. If it returns other values, it was going to make it more complicated. Especially when you have an if statement with multiple conditions that must be ORed or ANDed.
What is awful about the variable syntax? This syntax was copied directly from C language.
Uh, no. The condition itself is true or false. The return value is useful for assigning, passing as parameter, etc. Non-returning if requires dealing with temporary variable, which itself is verbose, doesn't automatically get assigned in both branches, doesn't play nice with C++-style move semantics, etc.
> What is awful about the variable syntax? This syntax was copied directly from C language.
C language is awful, but at least they have the excuse that nobody knew better in the 70s and const kind of works even the syntax is a bit complicated.
>>> public static void main(final String[] args) { System.out.println("Hello world!") }
Is just a glamour shot. How many times does one actually have to type public static void main? Once per application, by definition.
that's a good thing, because intellij massively increases your productivity
My main gripe with Kotlin is some of its design choices for syntax. I really really really don't like its syntax for lambdas, specifically. A code block in curly braces could be both a lambda with an implicit argument, or a list of statements. You have to inspect the context to see which one it is, or rely on intelliJ to make the former type slightly bolder. That is so bad for comprehension, and I think the only reason it was done like that was to allow for some cute hacks where control flow constructs such as map, fold etc. could be implemented using lambdas and still look like regular imperative code.
Another thing is that you can create lambdas with an implicit binder that you can refer to as "it" inside the body. It may save you one or two keystrokes, but it also encourages the programmer to be lazy about naming things. Combined with the fact that curly braces are overloaded, whenever I see "it" in code, my eyes scan frantically for the first opening curly brace, and I then have to determine whether it is a lambda with an explicit binder (then it isn't my binder, and I have to continue searching), a statement block (then it also isn't my binder), or a lambda with an implicit binder. Don't get me started on nested lambdas with implicit "it" binders that shadow each other.
val page = html {
head { title = "Title" }
body {
etc
}
}
It looks like it's a part of the language and naturally integrates with features like code folding, but it's actually all just functions and objects anyone can create.DSLs have taken off in one area: UI construction. There's TornadoFX for JavaFX, SwiftUI on Apple and JetPack Compose for Android. I've tried both new and old approaches extensively and to be honest I'm not sure this is actually better than using a proper GUI builder, but for small fragments of UI it's pretty neat. Sort of like what JSX does in React but more principled and type safe.
if (nullableVariable != null) {
boolean success = nullableVariable.someMethodCall()
if (success) {
return success
} else {
return fallbackIfNullMethodCall()
}
} else {
return fallbackIfNullMethodCall()
}
// Wouldn't that work just as well ?
if (nullableVariable != null) {
return nullableVariable.someMethodCall() || fallbackIfNullMethodCall();
}
return fallbackIfNullMethodCall();
Ok, that is still more cluter than the kotlin elvis operator, but more fair, isn't it? return Optional.ofNullable(nullableVariable).map(v -> v.someMethodCall()).orElse(false) ? true : fallbackIfNullMethodCall();return Optional.ofNullable(nullableVariable) .map(v::someMethodCall) .orElse(fallbackIfNullMethodCall);
That also looks much easier to understand, at least to me.
And the ternary operator is just odd when there is a more terse "standard" approach to it.
> The quote is either wrong or outdated. In the second edition, it's on page 521: "Industry average experience is about 1 - 25 errors per 1000 lines of code for delivered software. The software has usually been developed using a hodgepodge of techniques.
1-25 is not a single number - it is a range.
More info: https://karolgalanciak.com/blog/2017/09/24/do-or-do-not-ther...
The Java types become platform types
Now I cannot "unsee" that concept. Why isn't if/else an expression in other languages that just yields a value?! That's a powerful idea.
LISP. For a more recent example, Rust.
Well, yeah, Scala obviously, the primary language from which Kotlin drew inspiration for its syntax and features.
That’s one thing I like about Rust: it fuses functional and imperative styles very naturally.
[0] “ML” here meaning the family of languages including SML and OCaml, which inspired (to varying degrees) Haskell, Rust, some parts of modern C++, and surely others. Not “machine learning”.
In the workplace, however, the problem for me are the fearful developers who think it is too difficult to maintain a multi-project code base that contains more than one language. I want to continually grow and improve and branch out, sadly I find many I work with who would rather just skate by on what they know (in this case, Java, which I helped to mentor).
I haven’t yet found a way to convince them. I feel the only way I’ll be able to use Kotlin within the workplace is by changing jobs.
Java is still a great language, and the new release model is helpful to get some of the more modern language features found in other languages sooner in the Java ecosystem. And while JEP is a fine governance model for feature development, I still find myself wanting more, sooner, than what it produces.
I welcome any thoughts or suggestions on how to convince others of the usefulness of introducing Kotlin into the workflow. The idea I attempted was that I would begin a new project with Kotlin that, to start, only I maintained — to make it easier for others to begin to absorb the language, at their own pace. It was futile and they simply don’t want to try.
I am sorry I don’t have any suggestions to fix your problem but I just wanted to provide a bit of perspective. Maybe try thinking from your peers perspective to see what benefits adopting this new language would give and be prepared to answer why Kotlin among a whole class of JVM languages - Scala, Kotlin, Groovy, Clojure.
Thanks for your thoughts, though, I’ll take them into account.
For instance, in Kotlin you can perform operations on collection without converting them to a stream first, and collecting them with a certain collector afterwards. So in order to perform a single filtering operation on a collection, you need to do three steps in Java and just one in Kotlin.
Another advantage is coroutines, which not only allow you to write asynchronous code in a common, synchronous manner, but also get rid of shared mutable state, since coroutines can send/receive shared state through channels.
E.g. if you want to parse a CSV or JSON, you would google "how to parse it in Java", not "... in Kotlin". That's different, from e.g. Scala, where everything should be Scala-way (of course you could use Java libs in Scala, but it was always second-class I feel).
Also it is funny to see how Kotlin or Swift are marketed as simple languages when they managed to literally copy 90%+ of Scala features and make some actually even more complex (eg nullability).
If you say "overcomplicated" it suggests you have a simpler solution for the same problem in mind. So, what exactly would you simplify in Scala? Which unnecessary features would you remove? It is easy to throw an "X is overcomplicated" statement but much harder to point to concrete things that could be done differently. Sure, some stuff could be easily removed, but not without making the language less expressive.
I'm now embracing the JVM, and Frege is not an option. It'll be a mid-term transition for me. But it's already nice to rediscover some functional paradigms in Scala and apparently sane design choices when it comes to mixing the object-oriented with the functional style, at least I think I like it better than OCaml.
I'm bothered by Scala's performance as compared with Java. Kotlin's seems to be in parity with Scala in this context, but both by an order of magnitude off from Java's. However, I can't vouch for the "benchmarks" I've come across and I'm not yet familiar enough with either to conclude which features could be detrimental to JVM's performance. I would wholeheartedly appreciate your take on that.
Scala standard library is a bit in the baroque side, but it has been simplified recently in 2.13.
That. There's no "Kotlin way", there's only Java way. That's what I mean.
https://github.com/JetBrains/kotlin-native
does this mean end user can run without JRE? Does Java proper have something similar?
I haven't heard of kotlin-native, but apparently it isn't using GraalVM under the sheets which is interesting.
GraalVM native images are a different matter. It uses the Java memory model and ahead of time compiles all the code to native. The "VM" part is bundled with the binary but it's very small and thin. Really it's just a simple generational GC and a bit of support for reflection over things you said you want to reflect over.
Both produce small binaries that start as fast as C programs do. In fact the GraalVM team have advertised cases where native-image binaries start faster than C programs do.
Android ART is, effectively, a native compiler for a Java-targetted (but not JVM) bytecode too.
But yes this goes down to llvm. You can also import and compile c/c++ libraries via headers. I've been doing it recently.
This is the strength with multi platform declarations. you can write a back-end service then pick the best run time for that component.
There's also a javascript transpiler and tools to adapt typescript definitions so you can use Kotlin for react work or node.js work as well.
False, scala is really concise.
> slow compile times
A few seconds slower on average, you mean
> worse compatibility with other JVM languages.
Its completely backwards compatible with Java.
- Scala has a lot of cool, but sometimes confusing features. Implicits, companion classes, apply/unapply.
- our compile times are 5-10 minutes in CI. I'm sure we can make it go faster, but it requires digging into a 500 line SBT file.
- Scala seems to encourage building tangled webs of actors with Akka. I understand the benefits of actors. I think the benefits are overstated vs threads when you need less than 10k threads or so.
If you're a good programmer who writes well engineered code, you can do that incredibly well in Scala. Good practices * Scala's power = Great productivity + really good work.
Implicits are kinda harder to use, but this is an advanced feature meant for library writers, not application programmers. Despite their power I find them much nicer to use than c++ templates. And there is nothing comparable in Kotlin, and insufficient abstraction can sometimes lead to actually more complex code than in Scala.
As for compile times, Scala with Bloop is the fastest of JVM languages I ever saw. We switched to compile Java with Bloop because it is orders of magnitude faster than Maven/Gradle. Bloop works for Java and Scala, but not Kotlin. So Kotlin, having really nothing comparable at the moment, is currently the slowest to compile from these three languages.
> Scala compatibility with JVM is the SAME as Kotlin's.
Then you follow up with:
> Scala has also a few more non-Java features
These are contradictory claims. OP had a very good point about Scala being a more powerful footgun. Unfortunately, consciously avoiding features is not a matter of "just", and rarely works out in practicality.
And you need to avoid some features only in code that gets called from Java, not the whole codebase. Calling it this direction is very rare and not really a problem. Most codebases call Java from Scala and this is easy and fully supported.
Code bases with Scala tends to be unreadable for people not well versed in the language. Kotlin is mostly just a better Java. So it's easier to introduce. And the interop is first class, so it's really easy to mix and match java and kotlin, which also makes it easier to gradually introduce.
This is a sign the language is pulling its weight. We're going to spend most of our careers at the top of the learning curve, so we shouldn't optimize for briefly being at the bottom.
If Android Java was actually modern Java, the sales pitch wouldn't be as strong.
If you read between the lines, what they are saying is that Kotlin is now the primary language to use Spring with.
Kotlin is nice, and on some days preferrable, but the fact of the matter is, it's really not that much more legible or succinct than Java in the long run.
Look at Androind BTE docs [1] - the K vs. J examples side by side, there really isn't that much difference.
In the end, I feel K is just a 'different flavour' of Java, and often I feel that the makers of K are just throwing a bunch of ideas together, without necessarily having a philosophy.
Given a choice between a 'world of J or K' - to me, it's kind of a toss up - but in reality - J is the incumbent. You have to know J and deal with J at every step, all sorts of libraries are in J etc. etc. - so in reality, the offer of Kotlin is really not Kotlin, it's always Kotlin+Java - meaning two languages and paradigms to support.
If this were 1996 and we had to pick between DVD and BlueRay, it would be a difference decision, but in the end, it's not really 'either or', it's J or J+K.
In the end, J is not that much more hard to write and the differences are negligible except for some specific things ... so I've switch back to J and don't miss K.
[1] https://developer.android.com/guide/topics/connectivity/blue...
Java will have a similar problem with CompletableFuture and third-party projects like Reactor used in Spring WebFlux.
However, Kotlin baking in language syntax for a competing concurrency model will exacerbate the split.
With Java already having type inference, concise lambdas, and with upcoming records that can already be faked in current versions via Lombok, the value proposition of Kotlin seems questionable outside of Android with its held-back version of Java.
Clojure is another story entirely, offering a very different and compelling way of thinking about computing on the JVM. By contrast, my developing view is that Kotlin is different enough to require learning something new yet not different enough from modern vanilla java to offer substantial advantages.
Does anyone have experience of emergency automatic conversion back to Java?
(Edit: Never used Kotlin but I like the look of it)
I've not used Kotlin but I would be more likely to if I knew that avenue was practical.
However note that Kotlin and Java code can co-exist very closely in the same codebase. In the same way you can incrementally port a Java codebase to Kotlin, so too can you incrementally port Kotlin to Java. There's just no automatic tool for it like there is Java->Kotlin.
People keep bringing up Scala as a "purer" language. This seems to be true in the minds of Scala purists mainly. I've done Scala, IMHO it's a very messy language that seems to result in unwieldy code bases that was made for wannabe functional programmers that need a halfway house on the way to a proper functional language (like Haskell or Elixir).
I predict there's going to be a lot of effort wasted porting old Kotlin code to Java 20 or some such.
Willing to place a longbet on it.
For one, CoffeeScript had great impact on JS, and still has a few advantages (along its disadvantages). Perhaps Kotlin will play the same role.
But I'm doubtful Java can evolve as JS did. For one, the philosophy and speed of development of Java is very different, and kinda set in stone. But perhaps more importantly, types are part of Java, and they need to be backwards compatible. I think this will make it extremely hard to adopt some of the sane defaults from Kotlin.
1. CoffeeScript required a compile step; JavaScript didn't. It also meant that you needed to do your debugging in JavaScript, and deal with a lot more leaky abstractions. On the other hand, Kotlin and Java both compile directly to JVM bytecode.
2. CoffeeScript had a lot of features that work very unpredictably, like a number becoming an array of numbers if you add more lines at the same indentation level (which also means that your array will surprisingly become not an array if you delete too many entries). A lot of people only used CoffeeScript because there was no alternative, rather than actually liking it, and they were the first to jump ship when ES6 gave them an alternative.
Kotlin doesn't have any of these issues. It really is an improved Java.
The main differences between Kotlin and Java are:
* Kotlin forces null checks.
* Kotlin arrays are invariant.
* Kotlin does not have checked exceptions.
* Kotlin has first class functions.
* Kotlin sealed classes don't really have an analog in Java, AFAIK.
I don't find data classes to be a big deal. The best thing about them is you get `copy()` for free.
This is a bad argument.
Now the only supported Java version is 11, so a lot of companies have moved to 11 the last year.
Still a lot of Kotlin I see from my Android colleagues suffers from all the CONSTANT_STRING_TYPED magic and onion architected* stuff either inherited from the libraries and API's or ingrained into their programming habits.
* many layers, and every time you peel off another layer you feel more like crying
Having worked with Kotlin I do love the language though. There are some features like extensions which I hate. But in general I love the syntax though I do not find any significant advantage of using it over java.
After that, there are probably a few places where idiomatic use of Kotlin library and extension functions can improve things. I tend to that as I stumble on opportunities to do so. This stuff is great for getting rid of boiler plate. E.g. Kotlin's internal DSL language features can replace a lot of ugly builder pattern code. Also having constructors with default arguments removes the need for having builders or having multiple constructors. Same for functions.
Converting streams to sequences and restructuring statements with nested returns to return the result of a complex expression helps to. Using when instead of if makes things nicer. Etc. Lots of stuff to use.
My programs are definitely shorter in Kotlin and safer (e.g. nullability). Shorter is correlated with lower maintenance cost in literature on software engineering. Some things that Java is ok with are compile errors in Kotlin. So, safer is better in my book as well.
let str = “foo”
let age = 10
Kotlin does this with val, which is great.How do Scala and Kotlin compare?
Next time I’m back on the JVM, I’d like to one of these two, perhaps both if they interoperate.
So much focus on readability otherwise, what happened there?
router.get("/api/article/:id").coroutineHandler { ctx ->
val news = newsRepository.get(ctx.pathParam("id")) // no need for await
?: return ctx.respondText(404, "Not Found") // can still use basic code constructs
ctx.respondJson(200, jsonObjectOf("news" to news)) // actually an extension method i wrote
}For someone who already knows Kotlin.
1) Main
def main() = println("Hello")
2) Variables:
val y = 1
3) Options (nulls are discouraged):
val name: Option[String] = None
4) Null 2:
//If bob is not null, get his age, or get a default age
val age = bob.map(_.age).getOrElse(0)
5) Elvis operator
Kotlin:
val result = nullableVariable?.someMethodCall() ?: fallbackIfNullMethodCall()
Scala:
val result = option.map(_.someCall()).getOrElse(defaultValue)
6) String interpolation in scala seems to be exactly the same as kotlin:
val name = "John"
val lastName = "Smith"
val text = "My name is: $name $lastName"
val otherText = "My name is: ${name.substring(2)}"
In addition, you can also have:
val longString = s"""
|This is line 1
|This is line 2, my name is $name
""".stripMargin
7) This shows the same multi-line strings as above, but its unclear if you can have variables in multi-line strings like I showed above in Scala
8) Ternary operator:
val text = if (x > 5) "x > 5" else "x <= 5"
Some bonus stuff which scala has:
9) Case classes:
case class Person(name: String, age: Int)
10) Pattern matching
person match {
case Person("bob", 13) =>
println("Matched bob with age 13")
case Person("sally", _) =>
println("Matched sally, age unknown")
case Person(name,age) =>
println(s"Matched unknown person with name $name and age $age")
case x:_>
println(s"Matched unknown item: $x")
}10) Equality which actually makes sense, i.e
val foo = "foo"
val bar = "foo"
foo == bar //true in scala, no need to foo.equals(bar)
val bob = Person("bob", 13)
val other = Person("bob", 13)
bob == other //true, based on values
val list = Seq(bob)
list.contains(other) //true, based on values
11) .par collections / way better higher order functions
val list = Seq(string1, string2,...)
//Do a parallelized search on list to find all strings <= 3 chars in length:
val shortStrings = list.par.filter(_.length <= 3).seq
12) Try:
val riskyOp = Try( doSomethingRisky() )
riskyOp match {
case Success(result) =>
println(s"Op succeeded with result $result")
case Failure(e) =>
e.printStackTrace
}Disclaimer: I wrote all these in the HN textbox, please excuse any typos
I'm genuinely interested, having spent the last few years with Haskell and having recently rediscovered and being incredibly awed by the JVM and its ecosystem, and pondering on whether I should refresh what I'd known of Java or just outright embrace Scala.
I don't have the time to convert their whole site right now, sorry, but you'll find the same pattern holds - Scala does everything more elegantly and concisely. Feel free to look up a Scala tutorial.
Somebody please invent another case sensitive programming language, we don't have enough of them already.
Worked with a few case smashers in the 80s. Smashing Unicode is hard, I guess. I don’t really have much of a preference for or against case sensitivity, though I do hate fuglyCaps