Fork me on GitHub
#clojure-dev
<
2018-12-18
>
andy.fingerhut22:12:07

I'm still digging, but fairly confused right now as to some weird behavior I am seeing around ##NaN

andy.fingerhut22:12:04

I have documented in the recently published Equality guide that not only is (= ##NaN ##NaN) is false, but that ##NaN included inside of any collection guarantees that they will not be clojure.core/= to each other, which I think is at least almost always true.

andy.fingerhut22:12:36

Today I found an exception, which I am sure I must have tried in a REPL before at some time in the past, but at least with Clojure 1.9 and 1.10 it is giving me a surprising result:

andy.fingerhut22:12:50

user=> (def s1 #{1.0 2.0 ##NaN}) #'user/s1 user=> (def s2 #{##NaN 2.0 1.0}) #'user/s2 user=> (= s1 s2) true

andy.fingerhut22:12:09

I will let you know if I find out why this is happening, but in case you beat me to the answer...

andy.fingerhut22:12:00

My best guess right now is that it is due to how the Java equiv method is implemented on persistent sets, and perhaps the fact that (.equals ##NaN ##NaN) is true.

andy.fingerhut22:12:12

Another possibility is that it is related to this behavior:

andy.fingerhut22:12:12

user=> (identical? ##NaN ##NaN) true user=> (identical? Double/NaN Double/NaN) false

andy.fingerhut22:12:20

Yeah, this combination of facts is pretty cool:

andy.fingerhut22:12:36

user=> (identical? ##NaN ##NaN) true user=> (= ##NaN ##NaN) false

andy.fingerhut22:12:18

"cool" in a mind-blowing, wait-a-second-that-can't-be-right kind of way, I mean.

favila22:12:34

wait, does ##NaN return a memoized boxed double NaN?

andy.fingerhut22:12:41

Seems yes. Or probably not memoized, just a unique object created when Clojure starts up an initializes, probably.

hiredman22:12:47

at start up it takes the value of NaN field on the Double class and sticks in it a map (so it gets boxed) and that same boxed value is returned by the reader everytime it reads ##NaN

andy.fingerhut22:12:18

For the equality guide, I think my plan is simply to soften the language around "always infects collections containing ##NaN" to "usually causes collections containing ##NaN to not be = to each other"

Alex Miller (Clojure team)22:12:43

##NaN is an interned symbol so does have identity

hiredman22:12:20

it isn't a symbol though, the reader turns it in to a Double

hiredman22:12:11

Clojure 1.10.0
user=> (type (read-string "##NaN"))
java.lang.Double
user=>

Alex Miller (Clojure team)23:12:03

right, and that value is cached in the reader