読者です 読者をやめる 読者になる 読者になる

マクロ練習第二弾 - Java オブジェクトのメソッド名とメソッドの値のマップを返す

本当はmethodnameに相当するkeywordを作ってマップで返したかったのですが、symbolからkeywordを作る手段が思いつかず、断念しました。

シンボルからキーワードは (keyword 'sym) で OK
元エントリのは単純なメソッドにしか使えないので、もうちょっと便利にしてみた (obj-values-3)
bean とか知らないので勘違いしている可能性アリ

マクロ定義

; 引数未対応版
(defmacro obj-values-2
  "Takes a Java object and method names, returns a list of method values."
  [obj & names]
  (let [o (gensym)
        kvs (->> names
                 (map
                   #(list (keyword %)  `(. ~o ~%)))
                 (apply concat))]
    `(let [~o ~obj]
       (hash-map ~@kvs))))

; 引数&メソッドチェイン対応版
(defmacro obj-values-3
  "Takes a Java object and method names, returns a list of method values."
  [obj & names]
  (let [o (gensym)
        kvs (->> names
                 (map
                   #(if (sequential? %)
                      (list (keyword (first %)) `(.. ~o ~@(rest %)))
                      (list (keyword %)  `(. ~o ~%))))
                 (apply concat))]
    `(let [~o ~obj]
       (hash-map ~@kvs))))

実行例

user=> (obj-values-2 (java.io.File. ".") toString hashCode canRead)
{:toString ".", :canRead true, :hashCode 1234367}

user=> (obj-values-3 (java.io.File. ".") canRead (can-read-str canRead toString) (eq (equals "hoge")))
{:eq false, :can-read-str "true", :canRead true}

つっこみ

  ([obj methodname & methodnames] `(concat (obj-values ~obj ~methodname) (obj-values ~obj ~@methodnames)))

この場合、obj が複数回評価されうるのでよろしくないと思います。

   `(let [obj# ~obj]
      (concat (obj-values obj# ~methodname) (obj-values obj# ~@methodnames)))))

こんなんが良い、かも?