4

All,

I want to create a function that takes a symbol representing a java method and applies it to some object:

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

When I execute this, I get a result much different from what I expect

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

2 questions:

1> why is the symbol m being called as the second arg of the '.' function instead of the value bound to m?

2> how would I actually do what I want to do?

3 Answers 3

12

It's not working because . is a special form and has special evaluation rules. Normal function calls evaluate their arguments, but . doesn't evaluate the method-name parameter.

To make it work, either use eval or change your function into a macro.

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"

Use of eval generally isn't recommended.

Sign up to request clarification or add additional context in comments.

Comments

2

The problem is that the method calls are hard-wired in the JVM bytecode. As Brian states, eval will work, because it compiles the code each time it is called. However stay away from eval. It has its uses, but this one is not one of them.

The best way is to do what you want is to use reflection. Or if possible use the macro Brian showed. Reflection can be done via:

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

Not really tested, though...

Comments

0

The macro Brian alludes to exists already, and is called memfn, for "member function".

user> ((memfn length) "test")
4

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.