"Jim - FooBar();" <jimpil1...@gmail.com> writes:

>>     (= (Boolean. false) false) ;=> true
>>
>> now, that is really weird.  By all means (at least those, I can come
>> up with ;-) it looks like false and (Boolean. false) are the same:
>
> I think when you use the = operator in Clojure it calls to the
> .equals() method for java Objects.  That is why the above works
> "correctly" while the if test condition "fails". Also when you call
> compareTo I'd expect the .equals() method to be invoked again...

Just to throw something in: at least the docs are pretty clear.

,----[ http://www.clojure.org/special_forms#Special Forms--(if test then else?) 
]
| Note that if does not test for arbitrary values of java.lang.Boolean,
| only the singular value false (Java's Boolean.FALSE), so if you are
| creating your own boxed Booleans make sure to use Boolean/valueOf and
| not the Boolean constructors.
`----

`if' only does a null-check for `nil' and a primitive `false' check (aka
Boolean.FALSE).  Here's its implementation:

--8<---------------cut here---------------start------------->8---
        public Object eval() {
                Object t = testExpr.eval();
                if(t != null && t != Boolean.FALSE)
                        return thenExpr.eval();
                return elseExpr.eval();
        }
--8<---------------cut here---------------end--------------->8---

One could easily add

  || (t instanceof Boolean && ((Boolean) t).booleanValue())

to the clause to remove that unexpected behavior.  That would mean, from
a theoretical standpoint, that `if' would be a bit slower in the falsy
and the "new Boolean()"-truthy case.

To check that, I've created the simple benchmark below that runs the
current and enhanced implementation 10 million times with a evenly
distributed set of numbers, Booleans "created properly" with valueOf(),
Booleans created with the constructor, Strings, and nulls.

To my amazement, the variant that explicitly checks for Boolean wrapper
objects is not at all slower.  On my machine with IcedTea7-2.1, the 10
million checks with either the current null/false-check or the
null/false/Boolean-check take about 155 milliseconds.

So at least from a performance standpoint, there doesn't seem to be a
reason not to do it.  It's just a semantic question: should
(Boolean. false) bo a truthy thing?  Right now, it is and the docs state
that.  But is that good, especially since this topic pops up every few
months on the list?  And equally important: is there existing code that
relies on (Boolean. false) being truthy?

Bye,
Tassilo

--8<---------------cut here---------------start------------->8---
public class IfTest {

        public static Object eval(Object t) {
                if ((t != null) && (t != Boolean.FALSE)) {
                        return 1;
                }
                return -1;
        }

        public static Object eval2(Object t) {
                if (((t != null) && (t != Boolean.FALSE))
                                || ((t instanceof Boolean) && ((Boolean) 
t).booleanValue())) {
                        return 1;
                }
                return -1;
        }

        public static void main(String[] args) {
                int MAX = 10000000;
                List<Object> l = new LinkedList<Object>();
                for (int i = 0; i < MAX; i++) {
                        double d = Math.random();
                        if (d < 0.2) {
                                l.add(i);
                        } else if (d < 0.4) {
                                l.add(Boolean.valueOf(Math.random() < 0.5 ? 
true : false));
                        } else if (d < 0.6) {
                                l.add(new Boolean(Math.random() < 0.5 ? true : 
false));
                        } else if (d < 0.8) {
                                l.add(String.valueOf(i * i * i));
                        } else {
                                l.add(null);
                        }
                }
                // For warmup...
                for (Object o :l) {
                        if (eval(o) != eval2(o)) {
                                throw new RuntimeException();
                        }
                }

                long time = System.currentTimeMillis();
                for (Object o : l) {
                        eval(o);
                }
                long time2 = System.currentTimeMillis() - time;
                System.out.println("eval(): " + time2);
                time = System.currentTimeMillis();
                for (Object o : l) {
                        eval2(o);
                }
                time2 = System.currentTimeMillis() - time;
                System.out.println("eval2(): " + time2);
        }
}
--8<---------------cut here---------------end--------------->8---

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to