3

I'm new to Clojure and have created a simple macro to call certain methods on a Java class and return results into a Clojure map and this all works fine. (Also i'm aware of the bean function however the classes I'm using aren't beans).

My question is in regard to the calling code. Is it better form to pass the Java 'methods' as symbols?

I'm not even sure what type .toString is at the moment (in the calling code)? It all works but not sure its idiomatic Clojure.

(defmacro obj-to-map 
  [obj & body]
  (let [afn (fn [[method kw]]
              `(~kw (~method ~obj)))]
  `(assoc {} ~@(mapcat afn (partition 2 body)))))

(obj-to-map "hello" .length :length .toString :value)  

=>  {:value "hello", :length 5}
2
  • Maybe github.com/ngrunwald/gavagai would be helpful as well. Commented Mar 6, 2014 at 16:20
  • Yeah saw this project but its probably overkill for what I need. Its also fun to work these things out for yourself sometimes! Commented Mar 6, 2014 at 23:58

2 Answers 2

2

The .toString etc are symbols in the calling code.

I think it would be better to pass the method name and invoke it using ".".

(defmacro obj-to-map 
  [obj & body]
  (let [afn (fn [[method kw]]
              `(~kw (. ~obj ~method)))]
  `(assoc {} ~@(mapcat afn (partition 2 body)))))

(obj-to-map "hello" length :length toString :value)  

=>  {:value "hello", :length 5}
Sign up to request clarification or add additional context in comments.

1 Comment

You're right, that looks a lot nicer. "clojure.org/java_interop#Java Interop-The Dot special form"
0

As an option consider solving this without macros. A macro would still implicitly use reflection, and here it is enough to apply clojure.lang.Reflector/invokeInstanceMethod

The basic implementation is obj-to-map followed by to simple modifications with var-args.

; just a reusable object for calling no-arg methods
(def empty_array (into-array Object []))

; function update-vals available since v1.11.50
(defn obj-to-map [obj methods]
  (update-vals methods
               #(clojure.lang.Reflector/invokeInstanceMethod
                  obj % empty_array)))

(defn obj-to-map2 [obj & methods]
  (obj-to-map obj (apply hash-map methods)))

(defn obj-to-map3 [obj & methods]
  (obj-to-map obj (into {} (for [k methods] [(keyword k) k]))))
    
(defn obj-to-map4 [obj & methods]
  (obj-to-map obj (into {} (for [k methods] [(keyword k) (str k)]))))

; usage

(obj-to-map "hello" {:len "length" :st "toString"})     ; => {:len 5, :st hello}

(obj-to-map2 "hello" :len "length" :st "toString")      ; => {:st hello, :len 5}

(obj-to-map3 "hello" "length" "toString")               ; => {:length 5, :toString hello}

(obj-to-map4 "hello" 'length 'toString)                 ; => {:length 5, :toString hello}

Docs: update-vals

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.