Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clojure instance? single argument

I am a little confused by the clojure instance? function. It seems quite happy to take a single argument. So

(instance? String) 

works fine, but always returns false.

Am I missing something here? I've done this twice in two days, and both times it took me a quite a long time to debug (yes, I agree, to make the mistake once might be regarded as misfortune, but twice looks like carelessness).

Why doesn't it break, with an arity error?

Note added later: As of Clojure 1.6 this has been fixed!

http://dev.clojure.org/jira/browse/CLJ-1171

like image 364
Phil Lord Avatar asked Nov 01 '12 11:11

Phil Lord


3 Answers

Interesting... even though instance? is defined in core.clj, it appears that there is special handling built in to clojure.lang.Compiler for (instance?) forms.

Compiler.java, line 3498:

    if(fexpr instanceof VarExpr && ((VarExpr)fexpr).var.equals(INSTANCE))
        {
        if(RT.second(form) instanceof Symbol)
            {
            Class c = HostExpr.maybeClass(RT.second(form),false);
            if(c != null)
                return new InstanceOfExpr(c, analyze(context, RT.third(form)));
            }
        }

I interpret that to mean that, when you compile/evaluate an (instance?) form, the function defined in core.clj is ignored in favor of the hard-wired behavior, which does interpret a missing second argument as nil. I'm guessing this is done for performance reasons, as a sort of in-lining.

Apparently this special handling only applies in certain cases (and I'm not familiar enough with the compiler to know what they are). As illustrated by Ankur's answer, there are ways of calling instance? that cause the function defined in core.clj to be invoked.

like image 191
Alex Avatar answered Nov 03 '22 13:11

Alex


I think it is a bug. If you define a new version of instance?, e.g.

(def
^{:arglists '([^Class c x])
  :doc "Evaluates x and tests if it is an instance of the class
        c. Returns true or false"
  :added "1.0"}
foo? (fn foo? [^Class c x] (. c (isInstance x))))

you will get the expected exception

user=> (foo? String "bar")
true
user=> (foo? String 1)
false
user=> (foo? String)
ArityException Wrong number of args (1) passed to: user$foo-QMARK-  clojure.lang.AFn.throwArity (AFn.java:437)
like image 38
Joe Lehmann Avatar answered Nov 03 '22 15:11

Joe Lehmann


If you look at the instance? code you will see that the method isInstance of Class is called:

(def
    ^{:arglists '([^Class c x])
      :doc "Evaluates x and tests if it is an instance of the class
            c. Returns true or false"
      :added "1.0"}
    instance? (fn instance? [^Class c x] (. c (isInstance x))))

Looks like under the hood, nil (or false) is considered as the default value for x parameter when passed to the isInstance and that returns false.

like image 2
Giuliani Sanches Avatar answered Nov 03 '22 14:11

Giuliani Sanches