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

解決方法 -> ClojureでJavaクラスのコンストラクタをapplyする

つまり apply-new は第二引数が「単なるデータとしてのシーケンス」なのか「関数や特殊形式など評価後の値を得るためのS式」なのかを判断してから、展開を行う必要がある。

どうすればいいのだろう?

最終的には ->> 式の中で apply-new を使いたいのだが、それには上記の問題を解決する必要がる。

こんな感じにすると良いと思います。

; 関数化するマクロ
(defmacro new-fn [class num]
  (let [args (take num (repeatedly gensym))]
    `(fn [~@args] (new ~class ~@args))))

; 普通の関数っぽくやる場合
(apply (new-fn java.util.GregorianCalendar 3) [2010 5 25])
(->> [2010 5 25] (apply (new-fn java.util.GregorianCalendar 3)))

; 元エントリっぽくやる場合
(defmacro apply-new [class num args]
  `(apply (new-fn ~class ~num) ~args))

(apply-new java.util.GregorianCalendar 3 [2010 5 25])
(->> [2010 5 25] (apply-new java.util.GregorianCalendar 3))

引数の数(3)を渡しているのが、いまいちに見えるが、
これを解決する(引数の数を知る)には、引数を展開しなければいけない。
しかし、これはマクロなのでその評価はコンパイル時に行われてしまう。
それが望みの動作とは思えないので、引数の数は渡す必要がある。
第二引数が何者か判断しても、結局同じ問題が起こる。

補足

(defmacro apply-new [cls & coll]
  (concat `(new ~cls) (first coll)))

こういうのを書くなら

(defmacro apply-new [cls coll]
  `(new ~cls ~@coll))

とするのが楽だと思います。

一見できた風の代物

(defmacro apply-new-evil [class args]
  `(let [args# (eval '~args)]
     (eval `(new ~~class ~@args#))))
(->> [2005 10 20] (apply-new-evil java.util.GregorianCalendar))

apply してるわけじゃないけど。
ただし、eval してるのでローカル変数の解決ができない。

 (let [args ["neko"]] (apply-new-evil String args))
; java.lang.Exception: Unable to resolve symbol: args in this context (NO_SOURCE_FILE:164)

結局、役に立たないっぽい。
そのときの context では eval できないかな?