+ 3

What's this error I'm getting in Kotlin?

Here's the code: fun main() { println(1 + if (1 == 1) 6 else 4.2) } /* Shows an error message. The code works if I change the first argument to "if (1 == 1) 6 else 4.2" or "1 + if (1 == 1) 6.0 else 4.2". */ What's happening here? Why does Kotlin need two similar types in the conditional expression when being added?

16th Jul 2021, 12:21 PM
Calvin Thomas
Calvin Thomas - avatar
8 ответов
+ 4
The comparison with Python is not really fair because Python is dynamically typed and interpreted language. Static typing has always more strict rules, but also better runtime guarantees for the programmer to avoid certain kind of bugs. For example think what you would do in the compiler's position with this expression: if (a > 0) 42 else "nothing" One side is an Int and another is a String. So what type is the result of the expression? Can I add another number to the result safely? The result will be of type "Any" because that is the closest common ancestor of Int and String. But the actual value inside, can be cast explicitly to either an Int, or a String.
16th Jul 2021, 3:50 PM
Tibor Santa
Tibor Santa - avatar
+ 4
After experimenting with this for a while and looking at the kinds of errors being thrown out, I've come to think that Kotlin has some weird, implicitly union-like type which allows a variable to hold either of multiple datatypes. The reason why your code errors out is because the compiler fails to find an add operator which works with a type which holds either int or double. If you dissect the return value and use the add operator accordingly, it works: var res = if (true) 6 else 4.2 when (res) { is Int -> res+=1 } println(res) I'm baffled by the lack of discussion I can find online regarding this behaviour, but I did manage to come across this thread which talks about functions in Kotlin returning data of different types, and in there similar usage of if-else expressions were mentioned. https://discuss.kotlinlang.org/t/in-kotlin-can-a-function-return-either-string-or-int/16421 This leads to sealed classes, where one can do stuff like: (cont)
16th Jul 2021, 2:32 PM
Hatsy Rei
Hatsy Rei - avatar
+ 4
(cont) ... I wanted to show some code snippets of sealed classes and how if-else expressions returning different types are used together, but I guess that sort of is out of scope for your query. Either way, I am no Kotlin expert, so take my findings with a grain of salt. Again, I'm fairly surprised by how hard to was to look into this, I guess that's what piqued my interest in the topic.
16th Jul 2021, 2:37 PM
Hatsy Rei
Hatsy Rei - avatar
+ 2
Whenever I wanna do something with the result of the conditional expression, Kotlin gives me an error when I set it up with an Int and a Double. This error message won't be shown if all the operations on the result are removed. Here's my code: fun main() { var num = readLine()!!.toInt() var rem = num % 24 println((num / 24) * 15 + if (rem < 6) rem else 5 + (rem - 5) * 0.5) // Doesn't work }
16th Jul 2021, 12:25 PM
Calvin Thomas
Calvin Thomas - avatar
+ 2
I believe the key to understand this, is how Kotlin determines the result of the ternary expression. I think it is the closest common ancestor of the two branches, so if you have an Int and a Double on each branch, then the result will have the type Number. Why the example code goes to error, is because the addition operator is not meaningful between Int and Number types... So the number must be cast into a specific subtype for the operation to make sense. You can also review how Smart Casting works, for example if the compiler has the guarantees from an if condition that a value can be cast into a specific type, then it will have no problem auto-casting it and using it in expressions. https://kotlinlang.org/docs/typecasts.html#smart-casts
16th Jul 2021, 2:49 PM
Tibor Santa
Tibor Santa - avatar
+ 2
Hatsy Rei and Tibor Santa Thanks a lot for your great explanations. I have no idea why the Kotlin compiler isn't "smart enough" to give me a Double as the result. This isn't the case in Python. Why does the compiler decide to convert the result to the type Number? The SoloLearn course must've included this.
16th Jul 2021, 2:57 PM
Calvin Thomas
Calvin Thomas - avatar
+ 2
Using the toDouble() cast function works: fun main() { var num = readLine()!!.toInt() println(if (num < 24) (if (num < 6) num.toDouble() else 5 + (num - 5) * 0.5) else (num / 24) * 15 + (num % 24) * 0.5) }
16th Jul 2021, 3:14 PM
Calvin Thomas
Calvin Thomas - avatar
+ 1
Tibor Santa I guess that I'd need to stop comparing apples and oranges! Thank you for the explanation.
16th Jul 2021, 4:16 PM
Calvin Thomas
Calvin Thomas - avatar