Why Does Kotlin/JS Return Different Results for === Than Does Kotlin/JVM?
Asked Answered
M

2

9

Given this code:

val value = "something"

println(value.toUpperCase().toLowerCase() == value)   // prints true
println(value.toUpperCase().toLowerCase() === value)  // prints false

On Kotlin/JVM 1.3.40, I get:

true
false

On Kotlin/JS 1.3.40, I get:

true
true

I would expect the same results on both, and I would expect the Kotlin/JVM results overall (as I should have different String objects).

Why am I getting different results based on runtime environment?

Marsipobranch answered 1/7, 2019 at 11:55 Comment(0)
S
4

This is because of how the runtime handles it.

On the JVM, == maps to equals, and === maps to == (identity checking), as outlined here. Meanwhile, JavaScript's equals operators are weirder. If you decompile your code, you get this with JS:

kotlin.kotlin.io.output.flush();
if (typeof kotlin === 'undefined') { 
    throw new Error("Error loading module 'moduleId'. Its dependency 'kotlin' was not found. Please, check whether 'kotlin' is loaded prior to 'moduleId'."); 
}
var moduleId = function (_, Kotlin) { 
    'use strict'; 
    var equals = Kotlin.equals; 
    var println = Kotlin.kotlin.io.println_s8jyv4$; 
    function main(args) { 
        var value = 'something';
        println(equals(value.toUpperCase().toLowerCase(), value)); // NOTE: equals
        println(value.toUpperCase().toLowerCase() === value);      // NOTE: ===
    } 
    _.main_kand9s$ = main; 
    main([]); 
    Kotlin.defineModule('moduleId', _); 
    return _; 
}(typeof moduleId === 'undefined' ? {} : moduleId, kotlin); 
kotlin.kotlin.io.output.buffer;

Now, if you consider the equivalent Java code (slightly shortened and without Kotlin):

public static void main(String[] args){
    String value = "something";

    System.out.println(value.toUpperCase().toLowerCase().equals(value));
    System.out.println(value.toUpperCase().toLowerCase() == value);
}

toUpperCase().toLowerCase() creates a new object, which breaks the == comparison, which is identity checking.

While === is outlined as identity checking as well, a === b is true if a and b are strings that contain the same characters. As you can tell from the decompiled Kotlin code, Kotlin.JS compiles to primitive Strings, not String objects. Because of that, the === in JS will return true when you're dealing with primitive strings.

Sketchbook answered 1/7, 2019 at 12:21 Comment(5)
Does this constitute a bug in the Kotlin/JS implementation? According to the Kotlin lang ref, "Referential equality is checked by the === operation (and its negated counterpart !==). a === b evaluates to true if and only if a and b point to the same object. For values which are represented as primitive types at runtime (for example, Int), the === equality check is equivalent to the == check." (kotlinlang.org/docs/reference/…) AFAICT, strings are not represented by primitive types (kotlinlang.org/docs/reference/basic-types.html).Munmro
@Munmro maybe. I'm not entirely sure tbh, because I'm not sure if it's intended to support differences in the underlaying native differences to some of the operators. I suggest you open an issue on the Kotlin issue tracker. If it isn't a bug, we'll at least get clarity as to whether it's by design or not.Sketchbook
Maybe @OP should open a bug report. :-)Munmro
@Munmro but in Kotlin/JS they are.Cranio
@Alexey Do you mean, in Kotlin/JS (1.3.40) strings are represented by primitive types? Yes I think we're agreed on that.Munmro
C
2

In JavaScript there are both primitive strings and string objects (see e.g. "Distinction between string primitives and String objects" in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String).

value.toUpperCase().toLowerCase() === value in Kotlin/JS compiles to value.toUpperCase().toLowerCase() === value in JavaScript (as you can verify by looking at the "Generated JavaScript code" tab at https://try.kotlinlang.org/). value.toUpperCase().toLowerCase() returns a primitive string. === on primitive strings is normal equality.

Cranio answered 1/7, 2019 at 12:12 Comment(3)
"it does in pure JavaScript, I expect Kotlin/JS to compile to it" -- but shouldn't Kotlin's transpiler generate code that gives the same results for pure-Kotlin operations that Kotlin/JVM does? How can we write Kotlin/Multiplatform projects if things like object equality differ by platform?Marsipobranch
For me, object equality is exactly the kind of thing you should expect to differ by platform.Cranio
@Alexey If the Kotlin language definition specifies certain behavior irrespective of platform, we should not expect the specified behavior to differ by platform.Munmro

© 2022 - 2024 — McMap. All rights reserved.