A big part of why Kotlin works is because IDE support is so fantastic now. And likewise, a lot of the reasons Java ties developers' hands is because there was no guarantee about the IDE they would be using.
As a trivial example, declaring variables. In Java, you have `final String foo = "bar";` while in Kotlin you can just say `val foo = "bar"`. They type is implied, but if you aren't sure, you can hover over `foo` and the IDE will tell you what the type is, so there's no longer a reason to type it out every time.
Also, you can now have multiple classes in a single file. When Java was created, the one-class-per-file rule made finding the source code to a file easy; just do a `find` for `Classname.java`. But with a modern IDE, you can just command-click an instance and be taken to the source code automatically.
Java's boilerplate is meant to make code discoverable, but the Kotlin IDE does that work for you, allowing developers to be more flexible.
var foo = “bar”;Java is way too verbose to be workable if I have to write out all of the class declarations and anonymous classes and “equals/hashCode/toString” boilerplate and “ExtremelyLongClassName foo = bar.extremelyLongMethodName(…)”; and there are too many pitfalls like comparing with “==“ instead of “Object.equals” and implicit null. Except that I don’t have to write out anything or worry about any pitfalls (well, implicit null still hits me sometimes, but @NotNull and @Nullable make it much less common) in IntelliJ. IntelliJ has such powerful and seamless analysis, it does aggressive code folding on Java 7 so that you are looking at code with “var”s and lambdas. It also has built-in support for popular libraries like Spring and Lombok.
IntelliJ single-handedly turns Java from a verbose, legacy language into something I could actually recommend and start new projects on (although at that point I usually go with Kotlin). And Eclipse, NetBeans have very deep Java analysis as well. Java may have started off without expecting IDE support but now IDE support is responsible for a large part of its popularity.
If I cannot access documentation for a library/function at a mere tap of a button - that language isn't going into my toolbelt. I have to code in like 5 languages at my dayjob, that's at least 3 different types of semantics for ==.
Java, and Kotlin, are great at that. It's the major pitfall for Scala(and a laundry list of others), on the other side(hey implicits and converters, that make even IntelliJ go "wut?"). Those long names in Java are actually a godsend. Give me "int indexInTheArray" over "int x" anyday. Explicitness of Python is also a pleasant thing to work with.
People forget that most software is boring website backends, business processes and similar tools. Most devs don't have mental capacity to devote to "the one and only" language+platform+ISA.
It might also be an unhappy coincidence, but the library I was playing around with (scrapy) also had HORRENDOUSLY documented code. With Java/Kotlin, you can just click into any method/class and you'll get a useful doc string 99% of the time. With Python, I had to read the (incomplete) documentation.
(But I do agree actually, a good IDE is much better investment than a minimal syntax revision)
Java is a surprisingly expressive language even though it does have a few warts.
I do think there should be a distinction on what is lumped as verbosity. Golang is vertically verbose with its error checking, and Java in my experience is at least more horizontally verbose (long lines).
So long as everyone on your team uses IntelliJ, then no problems. But not everyone uses IntelliJ, and now you've written code that is only easy to parse in a particular IDE...
For this reason, and being able to read my own code outside of just my IDE (say, in Github, etc), I've found it more necessary to be explicit with my typing in Kotlin for all but the most trivial snippets.
Besides, reading implicit types has _never_ been an issue on any team or collaboration effort in the 30 years of my programming experience and I find it _really_ hard to believe anyone that claims it is a bonified problem for readability. In fact the complete opposite is true for me, personally, that too much information in front of me becomes noise and the signal gets lost.
This is something I've learned about Groovy that I really like. You can declare variables in multiple ways (terms are my own):
foo = 1 # normal assignment
var foo = 1 # declared assignment
int foo = 1 # typed assignment
A lot of our current code used the first "normal assignment" that you would see in shell scripts, Python, whatever. There's nothing interesting about this.
I started using the second kind consistently. The "declared assignment" says "I am creating a new variable foo and its value is 1". If you are not creating a new variable (i.e. if the variable exists already) your program fails. This is a great way to ensure you're not overriding variables from earlier in the program or from another scope. My experience tends to be that this throws errors "now" for mistakes you're currently making (at this point in the control flow) by overwriting a variable that existed before.
The last is a "typed assignment", where you're not just saying "I am creating a new variable", but specifically the type of variable is important. This errors if you ever try to misuse a variable down the road, and helps with ambiguity. It solves the "somestring = somestring.split()" issue of discounting a variable as the potential issue due to "it can't be this one, that's a string" being right for a while and then wrong for a while (or vice-versa).
Anyway, all that to say: not having to specify data types is nice in some circumstances, but I've been leaning into adding data types to my code just to make absolutely sure I know what everything is all the time. Not having to in Kotlin sounds nice, but I hope there's a way to be explicit about everything.
var foo = "bar"; // you can add final if you feel like it
You could always have multiple classes in a Java file as long as there was only one public class.
People still think of Java as stuffed full of wordiness, slow startup, garbage collection seizing up code, and long compile times, but that’s all in the past.
Please correct me if I'm wrong, but I don't think Java's dependency discovery mechanism has evolved much (if at all) from prehistoric times (hence, the slow startup remains). Although GraalVM sort of addresses that, but in my understanding it's still hugely bound to Oracle and its draconian license terms.
I can’t recommend jetbrains rust support enough.
I similarly get irritated using anything else now.
Kotlin's type system is nominative and so while it is not as flexible as typescript (no intersection types, conditional types etc.) it also means that you don't run into those multi-page long type errors which need 5 mins of debugging to figure that some deeply nested object is null where undefined is expected.
It is particularly funny when the language server truncates the errors and it becomes impossible to infer the actual issue from the message. Every now and then I find myself extracting things out of objects and adding type annotations to simplify the errors. It is doable, but never needed in Kotlin.
[1]: https://github.com/microsoft/TypeScript/issues/14833
[2]: https://io.livecode.ch/learn/namin/unsound
[3]: https://arxiv.org/abs/1605.05274
No language is perfect. Your comment is absurd, and the citations are pedantic.
Same, but this was the case for C# in Visual Studio 20 years ago.
> I know of no other programming language that puts as much effort into context- and structure-aware refactoring
TypeScript + VSCode do all this and more.
This isn’t to say that Kotlin isn’t absolutely incredible, but I do not want to undersell the pleasure that C# is to work in with its tooling.
As a huge fan of Jetbrains products (I maintain a personal IDEA Ultimate subscription) this was still a huge turn off to me.
> I know of no other programming language that puts as much effort into context- and structure-aware refactoring
Do you mean JVM language? TypeScript, C#, and F# at least come to mind.
> Do you mean JVM language? TypeScript, C#, and F# at least come to mind.
As I mentioned in another comment [1] on this thread, soundness and decidability are non-negotiable for me. Types in C#, F# and TS are all Turing Complete [2], and therefor these languages are tooling-adverse in the sense that static analysis is fundamentally unreliable.Ideally, you want type inference to terminate in the amount of time it takes to type a few keystrokes to get realtime assistance, so decidability is still an over-approximation from a tooling standpoint and in practice anything strongly super-linear is not a fun experience to use.
> I know of no other programming language that puts as much effort into context- and structure-aware refactoring
To soundness and decidability.
Was there some study done where soundness and decability of Kotlin is provided?
> I still don't follow...
The premise was "toolability" in language design, which is one of Kotlin's main design principles. To do type-safe completion, navigation or refactoring, you need soundness, and to do it rapidly in the context of a live programming environment, you need to be able to resolve a type with keystroke latency. Rapzid asked [paraphrasing] "why not TypeScript, C#, or F#?" to which I responded that I do not consider these languages "toolable" because type checking is essentially broken (i.e., it might take forever, or give the wrong answer). > Was there some study done...?
No, not that I am aware of. If you find one, I would be keen to read about it.I don't understand how you can come to the conclusion that type checking is broken and the language is not toolable? Can you give an example of where Kotlin's tooling achieves something that is not achievable at the moment in a C# IDE?
(I've not used Kotlin much but I have never felt that the tooling for C# was lacking, so I am genuinely curious)
> Have you ever used VS/Resharper/Rider with C#?
Yes, and to be clear I have never encountered these issues in practice. I am also not claiming Kotlin has these properties, just pointing out the fact if you care about toolability, soundness and decidability are important factors to consider and if there is a language design issue, it makes me less confident in any tooling which is built around it.It comes down to whether you want type checking to work all the time, or most of the time? If your answer is "most of the time", then just use a dynamically typed language with GPT-based autocompletion. Most of the time completion and refactoring will work just fine.
Re: type checking in C#, maybe try the following example?
https://learn.microsoft.com/en-us/archive/blogs/ericlippert/...
This is bad advice. Such an approach will go wrong often enough to burn you, and it will go wrong silently, whereas the kind of issues you describe tend to lead to something far more visible (and therefore correctible) like a complier internal error or an IDE refusing to perform a refactor.
a) "most of the time" being 60% (very roughly what I'd expect when doing renaming beyond a single function in JS, and that's being optimistic). b) "most of the time" being 99% (very roughly what I'd expect in a typical C#/java/kotlin codebase that isn't using too much reflection).
Even aside from that, I'd argue that nullability, array covariance, and reflection all have MUCH bigger practical impacts on typechecking reliability than Turing-completeness. Most languages will simply fail to compile if the computed types require too much recursion, making the issue relatively moot.
Type-checkers don't actually need to solve the halting problem to be sound, they just need to guarantee completion for _most_ types of code, and anything that can't be guaranteed can simply be rejected.
> "most of the time" being 60%
I would expect this number to get closer to 99% with LLM-based AI code completion tools, so the gap between dynamically-typed languages and "good enough" languages that claim to be statically typed (but are in fact unsound) will become much less significant. But if you want correctness, you will need stronger guarantees. > I'd argue that nullability, array covariance, and reflection...
I would mostly agree with that statement, with the caveat that it depends on what you want from your type system. Personally, I want the checker to be (1) sound and (2) decidable and (3) fast. For everything else there's runtime type checking. > Type-checkers don't actually need to solve the halting problem to be sound, they just need to guarantee completion for _most_ types of code
Although I don't see much difference between undecidable type systems and dynamically typed languages, as I wrote, I see undecidability as less of an issue. My main concern is unsoundness, which is more critical and unclear how to fix. By "soundness" I specifically mean the formal property that "well-typed programs cannot go wrong". "Most types of code" is just a statistical claim, in which case you might as well use Python or JS/TS with a linter. Too many statically-typed languages decide to go off and roll their own types without reading PL literature, then end up with dealing with these issues later down the line.How on earth do you consider Kotlin acceptable then? Kotlin's type system has never been shown to be sound or decidable and is visibly a lot more ad-hoc than any of that list (and if by some miracle it was sound or decidable last week, I'm sure whatever new feature they added this week broke it). I suspect the only reason it hasn't been formally proven to be Turing Complete is that Kotlin's developers haven't bothered to write a specification for it - as far as I can see your link's example for C# would translate directly into Kotlin with the same properties.
> link's example for C# would translate directly into Kotlin
T() is not possible for a generic T in Kotlin.These languages were stable, well-specified, and heavily studied for years before these cases were found. One person noodling around for a few weeks failing to find an example is not evidence of anything; a language being too obscure to have seen much academic study is not an indicator that it's likely to have a good type system, if anything it's the opposite.
> T() is not possible for a generic T in Kotlin.
T isn't generic in that code, it's just a class.
> One person noodling around for a few weeks
Well, I've been trying for about three years, but to be fair I'm a pretty slow programmer so you may have better luck. > a language being too obscure to have seen much academic study
Doesn't seem too obscure to me. https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=kotl... > T isn't generic in that code, it's just a class.
I think it needs to be a generic type in Kotlin for this to work, because otherwise it will dispatch to a single method. It's tricky to get Kotlin to do much compile time computation. Not saying that it's impossible, but LMK when you've actually tried it. Here's some sample code if you want to try encoding a Boolean logic:https://github.com/breandan/galoisenne/blob/2e465e7a753f6341...
Well, your own link proves it, doesn't it? Kotlin has nominal subtyping, contravariance, and generics, therefore its type system is undecidable.
> I think it needs to be a generic type in Kotlin for this to work, because otherwise it will dispatch to a single method.
That's the whole point - the type system has to decide which overload to dispatch it to at compile time, which it can only do by performing an arbitrarily complex computation. I ported that code directly to Kotlin and it doesn't compile but it "should", which, well, I don't know what you were expecting or what you'd accept as proof when we've already got an academic paper that proves the point.
> Well, your own link proves it, doesn't it?
That code simulates a bounded cellular automaton, which effectively reduces to a regular language, but that's about as far as I've been able to get. If it's possible to implement general recursion, I'm not quite sure how - yet. > I ported that code directly to Kotlin
Can you share your translation of Eric's code? Maybe we can get it to work.Well, Kotlin has both declaration-site and use-site variance. So surely that result applies.
> Can you share your translation of Eric's code? Maybe we can get it to work.
Ah I didn't mean that code, I meant the earlier C# one from SO. https://pl.kotl.in/4BrbZAmZG
https://youtrack.jetbrains.com/issue/KTIJ-12496/Provide-inte...
https://youtrack.jetbrains.com/issue/KTIJ-10606/Support-movi...
https://youtrack.jetbrains.com/issue/KTIJ-5063/Extract-prope...
Whenever I try to get into another language today, the lack of language-aware editing tools are my main source of frustration - specifically, I expect navigation, refactoring and completion to work flawlessly. I know of no other programming language that puts as much effort into context- and structure-aware refactoring - if another language does one day replace Kotlin, it will need to offer a comparable editing experience.