Does anybody find Kotlin's Type Casting disgusting?

I think the reason this works is that the division operator is overloaded for Double / Int → Double. Per the documentation, “Absence of implicit conversions is rarely noticeable because the type is inferred from the context, and arithmetical operations are overloaded for appropriate conversions, for example: val l = 1L + 3 // Long + Int => Long”

From https://kotlinlang.org/docs/reference/basic-types.html#explicit-conversions

But … is that not implicit conversion ? The operator implicitly converts one of the terms to produce a floating point value.

I’m really disappointed here, because when talking about implicit conversion, the main trap is to mix integer division operation in a floating point context, and here, Kotlin does not protect us at all.

The following code works and gives 0.0 as a result (playground 1.3.72 JVM):

println((1/2)*2.1)

However, the harmless unitary conversion to a bigger precision type does not compile.

val pixelX = 0
val pixelXAsSubPixelPrecision : Double = pixelX

I don’t understand such choice, because the main argument against implicit conversion is always the same (and for a good reason) :

cases where you’re partially doing Integer calculations like 1/2*1.1 (which will be 0.0 instead of 0.55 if you calculate it in this order)

So, yes, I really like Kotlin, but I cannot understand how JetBrains can tell implicit conversion is wrong but put it in core mathematical operators. In that subject, Java is way more consistent, because implicit conversion rules are clear and always apply the same, so at least you’re warned.

Imho, the only other way I can think of to properly manage integer/floating operation mixing is to go Python way by making distinct operators for division and floor division.

Operator overloading is not the same thing as implicit conversion, because it doesn’t work if there isn’t an explicitly overloaded version of the operator.

One reason implicit conversion is a problem is specifically because it conflicts with overloading:

fun foo( bar: Long ) {
    println("A")
}
fun foo( bar: Double ) {
    println("B")
}

foo( 5 ) // Does this print "A" or "B"? Neither, because it (correctly) won't compile 

This problem goes away if there’s another overload of the function:

fun foo( bar: Long ) {
    println("A")
}
fun foo( bar: Int ) = foo(bar.toLong()) // Explicitly treat integer as long
fun foo( bar: Double ) {
    println("B")
}

foo( 5 ) // Prints "A"

The difference here is that the decision of how to handle the ambiguity is with the developer (and this includes the developer of the standard library), rather than rules that are baked into the language and can’t be changed.

For operators like +, -, *, / there won’t be big obvious differences because the standard library defines those overloads (which you can also do).

When assigning variables, however, that is lost, but consider this: A variable assignment may actually be a function call, and therefore could be overloaded, so implicit conversions would cause problems.

However, this is not really a big issue because Kotlin can infer variable types. The code that you gave (which doesn’t work)…

val pixelX = 0
val pixelXAsSubPixelPrecision : Double = pixelX

…doesn’t actually have to get much longer when you make it work; you just have to rearrange it…

val pixelX = 0
val pixelXAsSubPixelPrecision = pixelX.toDouble()

It may feel a little awkward coming from other languages (it certainly did to me) but once you get used to it, it’s really not a big deal.

Having no implicit conversion, but operators overloaded in the standard library, works out to be a pretty good balance, is easy to remember, and the behavior is well defined and most importantly unambiguous.

3 Likes

You effectively make a good point.

Nonetheless, I’m still bothered by the divide operation automatically assuming floor division on integers. It’s one of the trap in java language that I’ve stumbled upon many times, and I’ve had hope that Kotlin would prevent it (either by making it return a floating point value for example, or going with two distinct operators for fractional and floor division).

But maybe such breaking change might not justify at this point.

I definitely get where you are coming from. The first programming language I worked with professionally had separate operators for floating point and integer division, so that approach comes naturally to me.

It’s only a trap until you get used to it though, and in most cases where I made the mistake, it was caught by the compiler anyway due to an incorrect type elsewhere, so I don’t think it’s a particularly serious problem, and definitely not worth a breaking change, considering how much Kotlin code exists in production these days.