On Sat May  5 16:43 2012, David Sletten wrote:
> Thanks for your response Daniel. You explain WHAT is apparently
> happening here. However, I am still struggling to understand WHY this
> is the new behavior. 

Yes, this is indeed a valid question.  I think the answer is that this
particular behaviour is an unintended side effect of the performance
optimizations introduced in Clojure 1.3.  By refusing to box numeric
primitives until it's absolutely necessary, the results are generally
much better performance for arithmetic code.

> The documentation for 'identical?' states:  Tests if 2 arguments are
> the same object. To me, (identical? x x) asks whether 2 references to
> the same object (the referent of x) are identical. Clojure 1.4's
> response suggests that in some cases, within a given scope, a local
> can refer to 2 different things. To be charitable, this is a
> counterintuitive result. It's obvious that (identical? (Double. x)
> (Double. x)) should return false, but that's not what I'm asking. To
> suggest that x is not identical to x (within the same scope where they
> refer to the same thing) violates one of the most fundamental laws of
> logic.

Well, arguably, this is part of the unfortunate fallout of the JVM's
disjoint type system between objects and primitives.  The key thing to
realise is that before Clojure 1.3, (let [x 2] …) resulted in x
referring to an object that contains the value of 2.  In Clojure 1.3 and
newer, the x in (let [x 2] …) now refers to a primitive long with the
value 2.

> You give interpretations of what is happening under the covers in both
> pre- and post-1.3 Clojure above. Your explanation appears to
> correspond to the observed behavior, but how did you come to this
> realization? Can you point me to where this issue is documented? I
> don't find any clues in the Clojure literature.

I don't think it's documented, not as such.  I just happen to be
familiar with a lot of implementation details.

> I see the following example in _The Joy Of Clojure_ (pg. 71):
> (let [x 'goat y x] (identical? x y)) => true
> 
> As you point out, this is also the behavior with cached integers (-128
> <= n < 127). However, the following does not make the issue any
> clearer:
> (let [x 123] (identical? x x)) => true

As we have established, the JVM's cache kicks in for this.

> (let [x 1234] (identical? x x)) => false

This is outside the range of the cache, the boxed values of x are
different.

> (let [x 1234N] (identical? x x)) => true

Here, you are explicitly creating a clojure.lang.BigInt, an object.
> 
> (let [x 8.9M] (identical? x x)) => true
> (let [x (Double. 8.9)] (identical? x x)) => true
> (class 8.9) => java.lang.Double

Again for these, you are explicitly creating objects.

> Furthermore, in _Clojure Programming_ (pg. 433) the authors write:
> [identical?] corresponds directly to == in Java. This is clearly not
> true in the example I presented. This code will print 'true' in all 4
> cases:
>         Double d1 = 8.9;
>         Double d2 = d1;
> 
>         double d3 = 8.9;
>         double d4 = d3;
> 
>         System.out.println(d1 == d1);
>         System.out.println(d1 == d2);
>         System.out.println(d3 == d3);
>         System.out.println(d3 == d4);
> 
> Of course, looking at the source for 'identical?' vindicates what these 
> authors have written:
> (defn identical? [x y]
>   (clojure.lang.Util/identical x y))
> 
> In clojure.lang.Util:
> static public boolean identical(Object k1, Object k2){
>     return k1 == k2;
> }
> 
> So apparently as far as Java is concerned, my example should return
> 'true'. Therefore something must be occurring in the reader that
> results in the explanation which you gave.

Not quite, you get the same behaviour in Java if you have to autobox the values 
like Clojure does:

public class Equals {
  static boolean eq(Object lhs, Object rhs) {
    return lhs == rhs;
  }

  public static void main(String[] args) {
    // prints true
    System.out.println(eq(1, 1));

    // prints true
    System.out.println(eq(127, 127));

    // prints false
    System.out.println(eq(128, 128));
  }
}


> To be fair, the Common Lisp standard seems goofy to me on this issue
> too. The analogous operator is EQ, documented here:
> http://www.lispworks.com/documentation/HyperSpec/Body/f_eq.htm
> 
> Of note is the example below:
>  (let ((x 5)) (eq x x))
> =>  true
> OR=>  false
> This states that a conforming system may return either a true or a
> false value in this case. This doesn't make any more sense to me than
> what Clojure is doing, but all of the Common Lisp implementations I've
> tested (Allegro, Clozure, SBCL, CLISP) do return T as I expected.

So, in the end, the question is: is this a bug?  I can't speak for the
rest of Clojure/dev on this, but I am guessing that it might not be
considered a bug.  To be fair, it would be nice if your sample code
returned the intuitive answer.  However, I think the main argument
against it being considered a bug would be that it doesn't make sense to
compare numbers for identity, just use =.

With numbers, if you use =, the compiler will actually use a fast
primitive equality test and sidestep any boxing, resulting in better
performance.

At least in my experience, it is relatively rare to actually use
identical? unless I really, truly care about object identity.

I hope this helps clarify the issue.

Sincerely,

Daniel

Attachment: signature.asc
Description: Digital signature

Reply via email to