13

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

2
  • You can also call instance? with more than 2 args: (instance? String "a" 0) -> true Commented Nov 1, 2012 at 17:20
  • (instance? String "a" 0) gives ArityException error Commented Aug 15, 2015 at 22:17

4 Answers 4

8

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.

Sign up to request clarification or add additional context in comments.

1 Comment

The fast path in the compiler (which emits an instanceof expression) is taken when the call is a direct call with a symbol that directly denotes a class.
3

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)

Comments

2

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.

3 Comments

This alone fails to explain the observed behavior, because other functions defined in this manner do throw arity exceptions when called with the incorrect number of args.
You're right. I have not tested with my own version of this function. My bad. And looks like this "issue" persist since Clojure 1.3: groups.google.com/forum/?fromgroups=#!topic/clojure-dev/…
Interesting. I searched for a ticket that was suggested by this thread, and couldn't find it. I guess it has fallen under the wire. I'll see if I can work out how to submit a new bug report. Failing this, I guess I will wrap instance? for now.
2

Hmm....interesting... all the below calls fails (which is how it is supposed to be):

user=> (.invoke instance? String)
ArityException Wrong number of args (1) passed to: core$instance-QMARK-  clojure.lang.AFn.throwArity (AFn.java:437)

user=> (instance? (type ""))
ArityException Wrong number of args (1) passed to: core$instance-QMARK-  clojure.lang.AFn.throwArity (AFn.java:437)

user=> (apply instance? String [])
ArityException Wrong number of args (1) passed to: core$instance-QMARK-  clojure.lang.AFn.throwArity (AFn.java:437)

user=> (#'instance? Long)
ArityException Wrong number of args (1) passed to: core$instance-QMARK-  clojure.lang.AFn.throwArity (AFn.java:437)

Event creating a new instance of "instance?" function object works as it is supposed to work:

user=> (def a (.newInstance (aget (.getConstructors (type instance?)) 0) (into-array [])))
#'user/a
user=> (a String)
ArityException Wrong number of args (1) passed to: core$instance-QMARK-  clojure.lang.AFn.throwArity (AFn.java:437)
user=> (a String "")
true

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.