0
votes

I can use memfn to create a clojure function that invokes a java function.

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

memfn requires that the function name be a symbol. I'm wondering if I can write a macro to invoke an arbitrary method whose name is provided as a string. That is, I'd like to be able to invoke the following:

(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

The only way I can think to do this involves using read-string.

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

Edit: A version using read-string and eval that actually works the way I want it to.

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

Am I missing a fundamental macro building tool to take the string that a symbol references and turn it into a literal without potentially executing code, as read-string might?

Thanks for any ideas!

2
I think it would be useful to go into how you imagine using such a feature. When people start needing eval to do this kind of metaprogramming, there is usually some alternate solution that would achieve the same goal, but it would require a different approach. e.g., instead of defining a var and then separately creating functions based on that var, you can sometimes let a variable at global scope, and within that let define macros that have access to that definition via lexical scoping. - amalloy
You don't need read-string for that, the 'clojure.core/symbol' function can be used to convert a string to a symbol - ChrisBlom

2 Answers

0
votes

There's no way to do this, with or without read-string. Your proposed solution doesn't work. The distinction you're really trying to make is not between string and symbol, but between runtime data and compile-time literals. Macros do not evaluate the arguments they receive, so even if fn-name is the name of a var whose value is "startsWith", memfn (or your memfn' macro) will only ever see fn-name.

0
votes

If you are interested in calling java methods only then you can rely on java.lang.reflect.Method and its invoke method.

Something like this should work for parameterless methods and would not require a macro.

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

((memfn' "length") "clojure")
;=>7