Подтвердить что ты не робот

Clojure экземпляр? единственный аргумент

Меня немного смущает функция clojure instance?. Кажется вполне счастливым принять один аргумент. Так

(instance? String) 

работает нормально, но всегда возвращает false.

Я что-то упустил? Я делал это дважды за два дня, и в оба раза мне потребовалось довольно много времени, чтобы отлаживать (да, я согласен, сделать ошибку однажды можно считать несчастьем, но в два раза выглядит небрежно).

Почему он не прерывается, с ошибкой arity?

Примечание добавлено позже: Начиная с clojure 1.6 это исправлено!

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

4b9b3361

Ответ 1

Интересно... хотя instance? определяется в core.clj, кажется, что в формах clojure.lang.Compiler для (instance?) имеется специальная обработка для (instance?).

Compiler.java, строка 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)));
            }
        }

Я интерпретирую это как означающее, что при компиляции/оценке формы (instance?) функция, определенная в core.clj, игнорируется в пользу жесткого поведения, которое интерпретирует отсутствующий второй аргумент как nil, Я предполагаю, что это сделано по соображениям производительности, как своего рода подкладка.

Очевидно, что эта специальная обработка применяется только в определенных случаях (и я недостаточно знаком с компилятором, чтобы узнать, что это такое). Как показано в ответе Ankur, существуют способы вызова instance?, которые вызывают функцию, определенную в core.clj для вызова.

Ответ 2

Я думаю, что это ошибка. Если вы определяете новую версию экземпляра?, например,

(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))))

вы получите ожидаемое исключение

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)

Ответ 3

Если вы посмотрите на instance? code, вы увидите, что метод Class вызывается:

(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))))

Похоже, что под капотом nil (или false) считается значением по умолчанию для параметра x при передаче к isInstance и возвращает false.

Ответ 4

Хм.... интересно... все приведенные ниже вызовы терпят неудачу (как это и должно быть):

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)

Событие, создающее новый экземпляр экземпляра? функциональный объект работает так, как он должен работать:

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