Brian Guertin
02/18/2022, 5:02 PMfun negate(value: Any?) = when (value) {
is Int -> {
println(value::class.simpleName)
-value
}
is Double -> -value
else -> Double.NaN
}
Calling negate(-5.6)
returns -5
. It hits the is Int
path! And the best part is, it prints "Double"Grégory Lureau
02/18/2022, 5:05 PMnumber
will returns true for is Int
and is Double
? If yes, it kinds of make sense, but a warning could be great đBrian Guertin
02/18/2022, 5:12 PMis Number
should return true, but the other's shouldn't, if Kotlin is storing them all as Javascript Number.Tim Oltjenbruns
02/18/2022, 5:44 PMTim Oltjenbruns
02/18/2022, 5:44 PMTim Oltjenbruns
02/18/2022, 5:44 PMGrégory Lureau
02/18/2022, 7:35 PMnumber
in Javascript, it's not boxed (afaik) so checking the type doesn't make a lot of sense.louiscad
02/19/2022, 5:53 PMDouble
, then it should be able to go down the right path based on a fixed is
check.Grégory Lureau
02/21/2022, 2:41 PMTim Oltjenbruns
02/21/2022, 2:44 PMBrian Guertin
02/21/2022, 9:34 PMnumber
will be lossy, that's why I believe it should only pass checks for the base type is Number
, forcing the conversion to always be explicit & visible in the code. You want to treat the JS number as a Double? use Number.toDouble()
, you want to treat it as an Int
? Use Number.toInt()
Brian Guertin
02/21/2022, 9:35 PMis Int
should be true for Javascript numbers...Tim Oltjenbruns
02/21/2022, 10:59 PMTim Oltjenbruns
02/21/2022, 10:59 PMGrégory Lureau
02/21/2022, 11:10 PMnumber
and not boxed (I guess for performance reasons). You can experiment by yourself if you want https://pl.kotl.in/YX0Y9ytmMTim Oltjenbruns
02/21/2022, 11:12 PMwhen
on the class's simpleName. đ
But that's not exactly idealTim Oltjenbruns
02/21/2022, 11:14 PMGrégory Lureau
02/21/2022, 11:18 PMYou couldÂA Int or a Double are going to be mapped to on the class's simpleName.when
number
, and number
will always returns "Double" so not feasible. You just have to box yourself the value in a crafted class so you ensure it's not reduced to number
.Grégory Lureau
02/21/2022, 11:18 PM(12.34f)::class.simpleName == "Double"
Grégory Lureau
02/21/2022, 11:20 PMTim Oltjenbruns
02/21/2022, 11:22 PMif (number % 1 != 0)
Grégory Lureau
02/21/2022, 11:22 PMTim Oltjenbruns
02/21/2022, 11:26 PMTim Oltjenbruns
02/21/2022, 11:26 PMif (number % 1 != 0)
in performanceTim Oltjenbruns
02/21/2022, 11:27 PMGrégory Lureau
02/21/2022, 11:35 PM%1 == 0
or using toInt()
to compare like this :
val value = 12f
val a : Any = value.toInt()
val b : Any = value
println("is Int: ${a == b}")
println("is Double: ${a != b}")
Both won't really work, because 12.0
for example is presumably considered as a Double by the developer, but actually is an Int for KotlinJS, because there is nothing behind the dot. (Once the value has been passed in an Any
variable indeed.)
Using a external on jsBitwiseOr() could work, cannot test that tho.Tim Oltjenbruns
02/21/2022, 11:38 PMGrégory Lureau
02/21/2022, 11:45 PM^
and 'Int.or' -> |
, not sure if we can find it somewhere like that đ€GrĂ©gory Lureau
02/22/2022, 12:05 AMTim Oltjenbruns
02/22/2022, 12:08 AMTim Oltjenbruns
02/22/2022, 12:09 AMTim Oltjenbruns
02/22/2022, 12:10 AMTim Oltjenbruns
02/22/2022, 12:14 AMTim Oltjenbruns
02/22/2022, 12:22 AMTim Oltjenbruns
02/22/2022, 12:27 AMfun main() {
val d: Any = 12.2 - 0.2
println(d)
println(d::class.simpleName)
}
Tim Oltjenbruns
02/22/2022, 12:28 AMTim Oltjenbruns
02/22/2022, 12:29 AMTim Oltjenbruns
02/22/2022, 12:33 AMfun main() {
val d = 12.0
println(d::class.simpleName)
val out = negate(d)
println(out::class.simpleName)
}
fun <T : Number> negate(number: T): T {
println(number::class.simpleName)
return number
}
prints âDoubleâ âIntâ âDoubleâTim Oltjenbruns
02/22/2022, 1:21 AM{"_low":5,"_high":0}
So according to that, I wrote a version of negate using generics, because that preserves the concrete type, if available at the call site. Which preserves the output of 12.0 being Double, if itâs statically known on the input to be Double.
fun <T : Number> negate(number: T): T {
println("typeof: ${js("typeof number")}")
val negated = when (number) {
is Double -> -number
is Long -> -number
else -> error("Don't know how we will get here")
}
println("number: ${number::class.simpleName}")
println("negated: ${negated::class.simpleName}")
return negated as T
}
println("input: ${input::class.simpleName}")
val negated = negate(input)
println("negated return: ${negated::class.simpleName}")
When input is val input: Double = 12.0
, we get Double printed on the outside, typeof number, and Double on the inside.
When the input is val input: Int = 12
, we get Int on the outside, typeof number, and Double on the inside. (It is represented as a floating point in JS, so no precision worries here)
When the input is val input: Long = 12
we get Long on the outside, typeof object (or node, could be different based on environment), and Long on the outside.
for 12.6 it returns correctly and we still get Double types all around.
even tested with the tricky 0.1 that often gives rounding errors, and it came out with -0.1!
Probably safest bet to go with
fun <T : Number> negate(number: T): T {
return when (number) {
is Double -> -number
is Long -> -number
is Int -> -number
is Float -> -number
is Short -> -number
is Byte -> -number
else -> error("Don't know how we will get here")
} as T
}
Even though testing shows only the first two are required. If the implementation of Kotlin changes at any point, this is hopefully more forwards compatible than just checking Double and Long. The order here does matter, Double needs to be checked before Int/Short/Byte etc because those will pass for all types represented by a JS number. The generics preserves the type of the input, and the unchecked cast should be safe.Grégory Lureau
02/22/2022, 8:39 AMJust a side node for readers, be careful with operations like that, for exampleCopy codeval d: Any = 12.2 - 0.2
0.1 + 0.2 - 0.3 > 0
, because operators are adding precision issues in the mix.
Interesting to know that generics preserve the original type, so it all depends if you are going to use the negate method from Kotlin (with types known at compile time, is
could be right) or from Typescript with Any or Number (then is
will be... a bit unexpected).
Anyway, I'll continue to think about number type check as a bad KotlinJS/multiplatform pattern, and I'll prefer boxing number
in specific class when I need to use the type.Tim Oltjenbruns
02/22/2022, 12:46 PMTim Oltjenbruns
02/22/2022, 12:49 PMTim Oltjenbruns
02/22/2022, 12:49 PMTim Oltjenbruns
02/22/2022, 12:50 PM