解決方法 -> 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 できないかな?