5

Here is an example where calling identity changes the returned value, which seems to me to indicate that the docstring "Returns its argument." isn't entirely true:

(let [x Double/NaN] (identical? x x)) ;=> false
(let [x (identity Double/NaN)] (identical? x x)) ;=> true

Is this expected? Or is it a bug with the identity function somehow?

Sam Estep
  • 12,974
  • 2
  • 37
  • 75
  • 4
    This is probably due to boxing - `Double/NaN` returns a primitive `double` which is boxed twice in the first example but only once in the second. – Lee Jun 27 '17 at 20:13

3 Answers3

8

You appear to have found an edge case involving identity, identical?, and primitive vs object equality. Note that in Java, java.lang.Double/NaN is a primitive:

public static final double NaN

But identical compares Java Objects:

; clojure.core
(defn identical?
  "Tests if 2 arguments are the same object"
  {:inline (fn [x y] `(. clojure.lang.Util identical ~x ~y))
   :inline-arities #{2}
   :added "1.0"}
  ([x y] (clojure.lang.Util/identical x y)))

// clojure/lang/Util.java
static public boolean identical(Object k1, Object k2){
    return k1 == k2;
}

Try this trick to force the NaN into a Double object instead of an unboxed primitive:

tupelo.core=> (let [x (Double. Double/NaN)] 
  (spyxx x) 
  (identical? x x))

x => java.lang.Double->NaN
true

I suspect that autoboxing of the primitive NaN which may/may not occur with different use-cases is the cause of the differences you are seeing.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48
  • The trick at the end is just the same thing as what this question is asking: you box NaN up in a Double, just as identity does in the question. It's not really a different thing. I think the only thing this answer is missing is an explanation of why both `identity` and `identical?` entail autoboxing. – amalloy Jun 27 '17 at 23:08
3

To add a little color to Alan's answer on boxing:

You may want to look into the == function, which is implemented this way:

public boolean equiv(Number x, Number y){
    return x.doubleValue() == y.doubleValue();
}

This performs a primitive comparison of two actual doubles. Your example, with ==:

(let [x (identity Double/NaN)] (== x x))
=> false
(let [x (identity Double/POSITIVE_INFINITY)] (== x x))
=> true

What's going on? Why is NaN == NaN false? Well, a primitive comparison using == should actually return false for NaN. It's strangely specified this way in IEEE 754 and Java behaves this way. It's the only "number" which, when compared to itself, does not equal itself.

As an aside, to see how object equality can be a strange thing in Java, see this:

(identical? 127 127)
=> true
(identical? 128 128)
=> false

This is because java caches the first 2^8 unsigned ints, so the 127s being compared are the same object in the first example, but the 128s in the second example are different objects. So, there are some gotchas to be aware of with checking for equality!

But the main takeaway here is: identity is working as it should! Just be careful when comparing things, as the notion of "equality" is not so straightforward!

Josh
  • 4,726
  • 2
  • 20
  • 32
  • This doesn't answer the question, which is about the fact that the returned values are different from each other, and not about what specifically those returned values are. – Sam Estep Jun 28 '17 at 00:26
0

Does identity "return its argument"?

It depends what you mean by the argument.

  • If it is the evaluated expression in the function call form, then not always.
  • If it is what the body of the function sees on the stack upon entry, then yes, it does.

The anomaly arises because of the way that Clojure calls functions.

  • Clojure functions are objects that comply with the IFn interface.
  • A Clojure function call translates into one of the many invoke methods - overloaded for arity - of the function object.
  • All of the invoke methods have Object parameters.

The upshot of all this is that every Clojure function call translates its every argument into some kind of Object - an identity operation except on primitives, which are wrapped in the corresponding Java class: long into Long, and so on.

So even the identity function, essentially (defn identity [x] x), does not return a primitive argument. It can't, because it never sees it.


For example, let's consider the expression

(inc 3)

The number 3 is surely a long. What type is (inc 3)? Let's ask Clojure:

(type (inc 3))
=> java.lang.Long

... a boxed Long object.

Hang on, are we sure that 3 is a primitive long?:

(type 3)
=> java.lang.Long

Aaaaaaagh! It's boxed too!

Not necessarily! You can't tell, because by the time the body of type sees 3, it is boxed, whether or not it was so to the reader/compiler. The Clojure documentation is silent on the point. It just says that numeric literals are generally represented as per Java.

So - in general - it's the evaluation mechanism, not a particular function (such as identity) that's responsible for boxing primitive arguments. This is auto-boxing.

What your example shows is that primitives are held as such, un-boxed, at least in let forms:

(let [x 1.0] (identical? x x)) ;=> false
(let [x (identity 1.0)] (identical? x x)) ;=> true

The fact that identical? is able to distinguish between the two boxings of 1.0 shows that it is held as a primitive double. (I've used an ordinary double, just to show that the behaviour is nothing to do with the special value Double/NaN).

Now let's try putting the number in a var:

(def x 1.0)

(identical? x x) ;=> true
(let [x (identity x)] (identical? x x)) ;=> true

It's boxed.

While we're here, auto-boxing is idempotent:

(identical? x (identity x)) ;=> true

The above adds little to what Alan Thompson's and Josh's answers and Alan Malloy's and Lee's comments comprise. I just felt they had hooked and played the fish, without actually landing it.

Thumbnail
  • 13,293
  • 2
  • 29
  • 37