3
votes

Suppose that we have a Java class:

public class Fun {
   public int foo () {
    return 1;
   }

   public  int bar () {
      return 2;
   }

}

The code below does not work in Clojure:

(defn f [method]
(. (new Fun) method)) 

(f foo)

producing an expected error (no questions here):

CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling...:

But if I try to quote the symbol

(f 'foo)

it produces a different but also expected error:

IllegalArgumentException No matching field found: method for class com.dullesopen.srl.FunCall clojure.lang.Reflector.getInstanceField (Reflector.java:271)

The error is expected because according to the documentation in the special form . (dot) the second argument is assumed to match either the name of the field or method, so it is not an actual symbol from Clojure point of view.

The approach below fixes the problem but I do not like extra memfn. For me it indicates that Java methods are not actually first class objects in Clojure.

(defn f [method]
   (method (new FunCall))) 

(f (memfn foo))

So the question is: is there a way to "indicate" to the special form . that the second argument should be somehow resolved/evaluated to get a method name? Or maybe there is another special similar to dot but with the second argument resolved?

1

1 Answers

3
votes

You are correct in your understanding that java methods are not actually first class citizens in clojure. You cannot pass a method around independent of it's class. The closest I can think of is to pass around functions that know about both the class and the method, or use a macro to build the call to the method for each class. If you don't like your method above (which looks perfectly fine to me) then this can be done directly with a macro though personally I prefer your approach:

user> (defmacro f [method]
        `(. (String. "asdf") ~method))
#'user/f
user> (f hashCode)
3003444

PS: memfn was replaced by the function literal syntax. It is very rarely used.