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