Can we consider implicit widening conversions of int to long, float to double, etc.
val x = intValue.toLong()
val y = floatValue.toDouble()
seems somewhat redundant.
Can we consider implicit widening conversions of int to long, float to double, etc.
val x = intValue.toLong()
val y = floatValue.toDouble()
seems somewhat redundant.
Yes, integer promotion is on our radar. No plans to implement it soon, though.
That is good news, but what about float promotion is that also on the radar?
And by not soon are we talking 6 months, 1 year or longer?
Not that it matters, once you go Kotlin you never want to go back!
What counts as a âsafeâ conversion?
Trivial cases such as those shown above are clearly safe, but as soon as you add even the slightest complexity, thereâs danger.â (The Java Puzzlers book has many examples of the gotchas lurking around numeric promotions.)â For example:
val l: Long = 100_000_000 * someIntValue
At what point is the promotion done?â The naĂŻve approach would be at the point itâs assigned to the Long â but then the multiplication would be done in Ints, and could overflow before the promotion, even though the variable would be big enough to hold the final result.
(Alternatively, if all Ints were to be promoted before multiplication, that would cause other surprises in intermediate values.)
What about mixed-type expressions?â If you multiplied a Short by a Float, what type of arithmetic would be done, and what would be the result type?
What about the assignment operators such as +=
and *=
â if it silently promotes the left-hand value before calculation, how does it handle overflow when demoting it back again for the assignment/
And what happens when the new unsigned typed get involved?â Those bring in a whole new range of nasty issues.â What type do you get if you multiply a signed number by an unsigned?â (What value do you get??)â Javaâs designers decided not to include unsigned numbers precisely to avoid this hornetâs nest!
There are a few cases which are clearly safe, such as strict widening promotions when passing a function argument.â But even there, what if you tried to pass an Int expression such as the multiplication above to a Long parameter?â Your expectations might differ depending whether you knew that the parameter was a Long, and whatever the language did could be surprising to someone.
Kotlinâs current approach of requiring explicit conversions everywhere is uncharacteristically long-winded, but itâs characteristically clear and safe: it avoids all the nasty gotchas that lurk in languages like C and Java â and it forces you to think through the issues.
The only completely safe improvement would be to make explicit conversions more concise.â But how could that be done in an elegant way?â The existing .toLong()
&c methods follow the existing naming convention; theyâre obvious, easily discoverable, and trivial to infer.â Youâd either need a shorter method name (which would be cryptic and ugly), or some new syntax (ditto).
The inclusion of bitwise operators in the standard library, instead of the core language, shows that we do less low-level bit-twiddling now than we did a couple of decades before (when Java was released).â Similarly, we probably donât do as much complex calculation â we tend to code at a higher level these days!â So the overhead of explicit conversions is less than it would have been a few decades ago.
Please donât add a lot of new gotchas to the language just for the sake of a few trivial safe cases.
Hi gidds,
All very valid points and I agree that maintainable code that conveys a developers explicit intent trumps all other considerations, however take the following scenario:
val hull = ConvexHull().build(model.extractPoints().map { (x, y, z) â Point3D(x.toDouble(), y.toDouble(), z.toDouble()) })
val hull = ConvexHull().build(model.extractPoints().map { (x, y, z) â Point3D(x, y, z) })
The graphics engine uses Float32 for performance but setup phase, such as convex hull generation, use Float64 for additional precision and stability.
The second line is more compact and I donât think it suffers from any nasty side effects.
For stuff like that I gennerally like to just add my own custom overrides.
fun Point3D(x: Float, y: Float, z: Float) = Point3D(x.toDouble(), y.toDouble(), z.toDouble())
For overriding constructors I just add an annotation to suppress the âname should start with lower case letterâ warning, canât remember itâs name right now. Intellij has a quickaction to add it.
Or if you think itâs save for your usecase you can even use something like
fun Point3D(x: Number, y: Number, z: Number) = Point3D(x.toDouble(), y.toDouble(), z.toDouble())
Sure you have to go though and add those overloads to your classes but this is not too much work.
Yep, thatâs what Iâm already doing, itâs not a major thing just a nice to have for safe widening ops, but I understand itâs low on the Kotlin enhancements pipeline, itâs on the radar so will track along my Kotlin journey.
Kotlin does not appear to require explicit conversions everywhere, the following compiles and runs without any warnings or errors:
val i: Int = 1
val l: Long = 4
val f: Float = -0.30826f
val t: Float = f * 4 - 1
val d: Double = l - i / 6.0 + f - 0.38348
println(t)
println(d)
Yes, rules for literals and variables are different. Homogenization is one of goals of the promotion.