# Why does identical? treat let-bound ##NaN differently from direct reference to it?

Consider the following code snippets evaluated with Clojure 1.11.1:

;; example 1
user=> (identical? Double/NaN Double/NaN)
false

;; example 2
user=> (let [x Double/NaN] (identical? x x))
false

;; example 3
user=> (identical? ##NaN ##NaN)
true

;; example 4
user=> (let [x ##NaN] (identical? x x))
false

Why does example 3 return true when all the other examples return false? Is this the intended behavior?

I assume this has something to do with with how Clojure sometimes uses unboxed math.

In my mental model for Clojure, identical? is basically Java's == and ##NaN is Java's Double.NaN. In Java, Double.NaN == Double.NaN evaluates to false. Thus I expected all the examples to return false.

Okay, my mental model of identical? is not accurate. It calls clojure.lang.Util/identical which has the following implementation:

static public boolean identical(Object k1, Object k2){
return k1 == k2;
}

When Clojure calls a function with such signature, it has to box the primitive values in the arguments. Doubles are boxed by calling Double.valueOf(double). The unusual equality semantics of NaN make the examples confusing, but in every case, identical? compares the reference equality of boxed values, not numerical equality.

By looking at disassembled byte code (see below), we can see what's happening. Here's summary.

• In examples 1, 2, and 4, each of the arguments to identical? is boxed individually.
• In example 3, ##NaN is boxed only once and the same boxed value is used as the both arguments of identical?.

Here are a few more examples. Note that the behavior of ##Inf matches that of ##NaN.

(identical? ##Inf ##Inf)          ; true
(let [x ##Inf] (identical? x x))  ; false

(identical? 1.0 1.0)              ; false
(let [x 1.0] (identical? x x))    ; false

(def my-one 1.0)
(identical? my-one my-one)        ; true

## Conclusion

Everything works as it should. My mental model about identical? was wrong. It's more like (Object)... == (Object) ... than plain ==.

Lesson learned: Use identical? only for reference equality.

## Disassembled examples

The code has been disassembled with clj-java-decompiler.

### Example 1 (identical? Double/NaN Double/NaN)

class user\$fn_line_1__254
Minor version: 0
Major version: 52
Flags: PUBLIC, FINAL, SUPER

public void <init>();
Flags: PUBLIC
Code:
linenumber      1
1: invokespecial   clojure/lang/AFunction.<init>:()V
4: return

public static java.lang.Object invokeStatic();
Flags: PUBLIC, STATIC
Code:
linenumber      1
0: getstatic       java/lang/Double.NaN:D
3: invokestatic    java/lang/Double.valueOf:(D)Ljava/lang/Double;
linenumber      1
6: getstatic       java/lang/Double.NaN:D
9: invokestatic    java/lang/Double.valueOf:(D)Ljava/lang/Double;
linenumber      1
12: invokestatic    clojure/lang/Util.identical:(Ljava/lang/Object;Ljava/lang/Object;)Z
15: ifeq            24
18: getstatic       java/lang/Boolean.TRUE:Ljava/lang/Boolean;
21: goto            27
24: getstatic       java/lang/Boolean.FALSE:Ljava/lang/Boolean;
27: areturn
StackMapTable: 00 02 18 42 07 00 1D

public java.lang.Object invoke();
Flags: PUBLIC
Code:
linenumber      1
0: invokestatic    user\$fn_line_1__254.invokeStatic:()Ljava/lang/Object;
3: areturn

static {};
Flags: PUBLIC, STATIC
Code:
linenumber      1
0: return

### Example 2 (let [x Double/NaN] (identical? x x))

Skipped; similar to example 4.

### Example 3 (identical? ##NaN ##NaN)

Note that the boxing happens in the static block.

class user\$fn_line_1__246
Minor version: 0
Major version: 52
Flags: PUBLIC, FINAL, SUPER

public static final java.lang.Object const__1;
Flags: PUBLIC, STATIC, FINAL

public void <init>();
Flags: PUBLIC
Code:
linenumber      1
1: invokespecial   clojure/lang/AFunction.<init>:()V
4: return

public static java.lang.Object invokeStatic();
Flags: PUBLIC, STATIC
Code:
linenumber      1
0: getstatic       user\$fn_line_1__246.const__1:Ljava/lang/Object;
3: getstatic       user\$fn_line_1__246.const__1:Ljava/lang/Object;
linenumber      1
6: invokestatic    clojure/lang/Util.identical:(Ljava/lang/Object;Ljava/lang/Object;)Z
9: ifeq            18
12: getstatic       java/lang/Boolean.TRUE:Ljava/lang/Boolean;
15: goto            21
18: getstatic       java/lang/Boolean.FALSE:Ljava/lang/Boolean;
21: areturn
StackMapTable: 00 02 12 42 07 00 17

public java.lang.Object invoke();
Flags: PUBLIC
Code:
linenumber      1
0: invokestatic    user\$fn_line_1__246.invokeStatic:()Ljava/lang/Object;
3: areturn

static {};
Flags: PUBLIC, STATIC
Code:
linenumber      1
0: ldc2_w          NaN
3: invokestatic    java/lang/Double.valueOf:(D)Ljava/lang/Double;
6: putstatic       user\$fn_line_1__246.const__1:Ljava/lang/Object;
9: return

### Example 4 (let [x ##NaN] (identical? x x))

Note two Double.valueOf calls in the invokeStatic method.

class user\$fn_line_1__250
Minor version: 0
Major version: 52
Flags: PUBLIC, FINAL, SUPER

public void <init>();
Flags: PUBLIC
Code:
linenumber      1
1: invokespecial   clojure/lang/AFunction.<init>:()V
4: return

public static java.lang.Object invokeStatic();
Flags: PUBLIC, STATIC
Code:
linenumber      1
0: ldc2_w          NaN
3: dstore_0        /* x */
5: invokestatic    java/lang/Double.valueOf:(D)Ljava/lang/Double;
9: invokestatic    java/lang/Double.valueOf:(D)Ljava/lang/Double;
linenumber      1
12: invokestatic    clojure/lang/Util.identical:(Ljava/lang/Object;Ljava/lang/Object;)Z
15: ifeq            24
18: getstatic       java/lang/Boolean.TRUE:Ljava/lang/Boolean;
21: goto            27
24: getstatic       java/lang/Boolean.FALSE:Ljava/lang/Boolean;
27: areturn
StackMapTable: 00 02 FC 00 18 03 42 07 00 1B

public java.lang.Object invoke();
Flags: PUBLIC
Code:
linenumber      1
0: invokestatic    user\$fn_line_1__250.invokeStatic:()Ljava/lang/Object;
3: areturn

static {};
Flags: PUBLIC, STATIC
Code:
linenumber      1
0: return
ago
Interesting. My mental model of `identical?` was accurate (`identical?` returns true for the same object), but my mental model of Clojure's parameter passing was flawed. Looking at your examples, here's what should have happened, IMO.

```clojure
;; example 1
user=> (identical? Double/NaN Double/NaN)
false
;; false is probably correct. If Double/NaN was marked final, then should be true, but it apparently isn't, though Double class is marked final.

;; example 2
user=> (let [x Double/NaN] (identical? x x))
false
;; IMO the Clojure compiler should box once and pass the same box to identical?, thus true

;; example 3
user=> (identical? ##NaN ##NaN)
true
;; I'm glad this is true but surprised that this is optimized as a special case.

;; example 4
user=> (let [x ##NaN] (identical? x x))
false
;; As with example 2, the Clojure compiler should be smart enough to box this once, thus true.
```

Basically, my intuition is that if `x` is let-bound to a value, that identical object, whether boxed or unboxed, should be passed to a called function. As a pure matter of efficiency, not having anything to do with `identical?`, the compiler should not be boxing the same primitive value multiple times. So, IMO, this seems like an issue with the Clojure compiler not generating either intuitive or optimal code.

In example 3, reader binds those two constants under the same Object constant, so there's just one instance of type Object.

And in other examples, there are instances of the double type.

With all that, it's just how Java works: https://stackoverflow.com/a/1408643/564509

An easy way to demonstrate the Java's behavior:

jshell> Object a = Double.NaN
a ==> NaN

jshell> a == a
\$2 ==> true

jshell> double a = Double.NaN
a ==> NaN

jshell> a == a
\$4 ==> false
How did you reach this conclusion?
If you mean the first two statements in my message, then I got your Clojure code, compiled it, decompiled it, and inspected the results.
Did you use clj-java-decompiler's decompile (Java output)? Try out disassemble (bytecode output) too; it doesn't really match your explanation.
I used the decompiler that's built into IntelliJ IDEA. But it shouldn't matter that much what you use. In the end, you can even inspect the byte code with some viewer. To increase the certainty, you can bind each expression in your OP to a name with `def`s.