Clojure, оценивающий строковую переменную как символ, это использование read-string хорошо?

Я могу использовать memfn для создания функции clojure, которая вызывает функцию Java.

(macroexpand '(memfn startsWith prefix))
=> (fn* ([target2780 prefix] (. target2780 (startsWith prefix))))
((memfn startsWith prefix) "abc" "a")
=> true

memfn требует, чтобы имя функции было символом. Мне интересно, могу ли я написать макрос для вызова произвольного метода, имя которого представлено в виде строки. То есть я хотел бы иметь возможность вызывать следующее:

(def fn-name "startsWith")
=> #'user/fn-name
(macroexpand '(memfn' fn-name "prefix"))
=> (fn* ([target2780 prefix] (. target2780 (startsWith prefix))))
((memfn fn-name "prefix") "abc" "a")
=> true

Единственный способ, которым я могу думать, заключается в использовании read-string,

(defmacro memfn' [fn-name arg-name]
  `(memfn ~(read-string fn-name) ~arg-name))

Редактировать: версия с использованием read-string и eval, которая на самом деле работает так, как я хочу.

(defn memfn' [fn-name arg-name]
  (eval (read-string (str "(memfn " fn-name " " arg-name ")"))))

Я упускаю фундаментальный инструмент для построения макросов, чтобы взять строку, на которую ссылается символ, и превратить ее в литерал без потенциально выполняющегося кода, как read-string может быть?

Спасибо за любые идеи!

2 ответа

Решение

Там нет никакого способа сделать это, с или без чтения строки. Ваше предлагаемое решение не работает. Различие, которое вы действительно пытаетесь провести, заключается не в строке и в символе, а в данных времени выполнения и литералах времени компиляции. Макросы не оценивают полученные аргументы, поэтому даже если fn-name это имя переменной, значение которой "startsWith", memfn (или ваш memfn' макро) увидим только когда нибудь fn-name,

Если вы заинтересованы в вызове только методов java, вы можете положиться на java.lang.reflect.Method и его метод invoke.

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

(defn memfn' [m]
  (fn [o] (.invoke (.getMethod (-> o .getClass) m nil) o nil)))

((memfn' "length") "clojure")
;=>7
Другие вопросы по тегам