3 ms·
I work in Kotlin and Java and have opinions on both. Some feedback... I disagree about the name shadowing issue. In fact, I wish Kotlin didn't suck so hard and
by kodablah 8y ago
I work in Kotlin and Java and have opinions on both. Some feedback...
I disagree about the name shadowing issue. In fact, I wish Kotlin didn't suck so hard and not allow me to ignore those warnings specifically. My primary use for name shadowing is so that I don't use the previous variable. This may be a bit strange, but in highly functional contexts with immutable variables, shadowing a name is reasonable especially as you begin to nest. It's not like Kotlin has a problem reusing/shadowing the "it" var name in nested blocks. Rust makes me very happy with how it handles it and it makes the code quite readable. Something like `val someValue = someValue ?: error("Bad value")` is very reasonable. If anything, you can have reasonable scoping warnings if there appears to be ambiguity, but it should not be across lambda blocks as it is now.
Can't cite type inference in Java if one of its most popular platforms doesn't support it. May be ok for you, not ok for everyone though. We might wish Android would keep up, but in the meantime we have practicality concerns.
I don't think those three nullability sigils make it too complex and has nothing to do with Scala where devs can define their own sigil operators. And Kotlin is ok about inference when the Java code has reasonable nullable/not-nullable annotations. There are a lot of problems with Kotlin's compile-time null checks (namely their insistence on thread safety so a null-checked field can't be reaccessed and assumed null), but "platform types" is the best that can be done within the constraints of existing JVM code.
Having the IDE put ::class.java in there is hardly a reason to go away. That's like saying I'm going back to Kotlin because sometimes in Java I have to type .class if its a type and .getClass() if its a value. The obvious reason for it is that Kotlin classes are supersets of Java ones and have more reflective properties. That and not everything is about the JVM (Kotlin has two other targets). If you can use KClass's, they are preferred.
There are too many articles on why languages like Scala, Go, Rust, etc choose to add the type to the right hand side of parameters/functions for me to re-explain it here.
Your complaints about statics are because how the JVM handles statics, not Kotlin. I appreciate the object approach over JVM statics. Just sucks that you keep having to go back to JVM-land where you are hamstrung. As your apps and library usage grows, you will move further away from the requirements forced upon you by Java and stop complaining about that being the reason you are going back.
The key-value not using a new operator thing is simply because one is defined as a piece of a library and not part of the language. You want them to write a special operator for maps which are not even part of the language (just a class that happens to be part of the stdlib)? Instead of an infix "to" that creates a Pair class? The disappointing part is wanting to extend the language syntax even further.
You wrote:
fun parseAndInc(number: String?): Int {
return number?.let { Integer.parseInt(it) }
?.let { it -> it + 1 } ?: 0
}
and complained about readability, when I see that and think:
fun parseAndInc(number: String?) = number?.toInt()?.inc() ?: 0
And I think, not only is that quite readable, but it sure isn't very practical to accept a null number here. Take a peek at the generated bytecode at some point too compared with your Java version. I'm a little concerned that this kind of stuff has you switching languages...if you make bad examples I bet they'll look bad.
In your back-to-Java article you mention data classes with no Java alternative.
Spring is annoying. If you're a Spring shop, and you're ok w/ all the magic, stay in Java. It might be best.
Sorry I typed so much. I have lots of problems with Kotlin too...but I sure don't share share the ones in the article.
- sixbrx 8y agoI agree about the shadowing. I find in some cases I need to refine a value to a more correct/cleaned/canonical form, but a separate method to establish an all-new scope is too much and would impact clarity. In those cases it's an insidious bug to accidentally be using an old/non-canonical form of something in the following code somewhere, mixed in with the uses of the refined form, just because the name can't be shadowed. It's very hard to spot because both forms seem to "read" correctly in isolation.
- bcoates 8y agoOptionally ignoring thread safety seems indefensible -- until you can go full COM and declare your package apartment-threaded, working in a mutable language with uncontrolled multi-threading is the deal with the devil you've made and code that doesn't embrace that reality is just broken. Agree about the shadowing. var x_2 = f(x_1) // never use x_1 again for anything deserves its own syntax and compiler checks
- kllrnohj 8y ago> Optionally ignoring thread safety seems indefensible Nobody forces everything to be thread safe. That would either be suicidally complex or suicidally slow. The problem isn't so much "ignoring thread safety", it's that this: class Foo(var thing: Int?) { fun doSomething() { if (thing != null) { thing++ } } } doesn't compile and it should. It fails to compile claiming that 'thing' could have changed in the meantime, so it can't safely assume non-null. However, that can only happen if Foo is accessed from multiple threads, but this isn't thread safe nor pretending to be in the first place. You can make the compiler happy by doing this: class Foo(var thing: Int?) { fun doSomething() { val t = thing if (t != null) { thing = t + 1 } } } But of course that's not remotely thread safe, either. It wasn't thread safe to begin with, and it's still not thread safe now. But this will of course compile just fine. You're forced to jump through hoops to workaround compiler "bugs" To be fair here the warning isn't actually about thread safety at all, it's to guard against something like this: class Foo(var thing: Int?) { fun otherThing() { thing = null } fun doSomething() { if (thing != null) { otherThing() thing++ } } } Kotlin has so far just been unwilling to allow the compiler to handle the cases where it could prove that the variable hasn't been modified in the meantime because they can't always prove it.