2009-11-12 2 views
4

Все,принимает имена методов Java в качестве функции арг в Clojure

Я хочу создать функцию, которая принимает символ, представляющий собой метод Java и применяет его к некоторому объекту:

(user=> (defn f [m] (. "foo" (m))) 

Когда я выполняю это , я получаю результат сильно отличается от того, что я ожидал

user=> (f 'getClass) 
java.lang.IllegalArgumentException: No matching method found: m for class java.lang.String (NO_SOURCE_FILE:0) 

2 вопроса:

1> почему символ m называется вторым аргументом '.' вместо значения, связанного с m?

2> Как я мог бы делать то, что хочу?

ответ

12

Это не работает, потому что . является special form и имеет специальные правила оценки. Обычные вызовы функций оценивают свои аргументы, но . не оценивает параметр имя метода.

Чтобы заставить его работать, используйте eval или измените свою функцию на макрос.

user=> (defmacro foo [o m] `(. ~o ~m)) 
#'user/foo 
user=> (foo 123 toString) 
"123" 
user=> (defn bar [o m] (eval `(. ~o ~m))) 
#'user/bar 
user=> (bar 123 'toString) 
"123" 

Использование eval обычно не рекомендуется.

+0

+1 на макрос. Нет необходимости в eval. –

2

Проблема в том, что вызовы метода жестко подключены к байт-коду JVM. Как утверждает Брайан, eval будет работать, потому что он компилирует код каждый раз, когда он вызывается. Однако держитесь подальше от eval. Он имеет свое применение, но это не один из них.

Лучший способ - сделать то, что вы хотите, использовать отражение. Или, если возможно, используйте макрос Брайан. Отражение может быть сделано с помощью:

(defn f 
    [m & args] 
    (clojure.lang.Reflector/invokeInstanceMethod obj m (into-array Object args)))

Не совсем испытано, хотя ...

0

Макрос Брайан намекает уже существует, и называется memfn, для «функции члена».

user> ((memfn length) "test") 
4 
Смежные вопросы