メタデータのこれまでとこれから
最初に謝らなければならないことがあります。ごめんなさい。私が間違っていました。
耳当てつきのvarはダイナミックスコープを持つと自動的にみなされない - OGINO Masanori@はてなで、:dynamicメタデータの値をtrueにするために、このように書きました。
(def ^:dynamic *fred*)
^:dynamicという書き方は1.3.0から追加されましたが、これを1.2のREPLに入力しても例外は発生しません。
(clojure-version) ;=> "1.2.1" (def ^:dynamic *fred*) ;=> #'user/*fred*
私はこれを見て「1.2でも動くんだな*1」と思い、そう話しました。
私が間違っていました。コンパイラに渡すと「Unable to resolve classname: :dynamic」という例外が発生します。
以下にその理由と対策を示します。
これまでのメタデータ
1.0では、^fooは(meta foo)に展開され、varにメタデータを付加する際には#^を使いました。
;; (meta #'str) ^#'str (defn #^{:tag String} long-hello [#^{:tag String} your-name] (str "hello, " your-name))
この:tagメタデータは型を指定するもので、コンパイラにリフレクションを使わないコードを生成させるために付加します。
この:tagメタデータには#^Klassというショートカットがあり、#^{:tag Klass}に展開されます。
(defn #^String short-hello [#^String your-name] (str "hello, " your-name))
しかし、1.1で^fooは非推奨となり、1.2で削除されました。用途がなくなった^は#^の新しい書き方として再定義され、1.2で#^は非推奨となりました。
(defn ^{:tag String} long-hello [^{:tag String} your-name] (str "hello, " your-name)) (defn ^String short-hello [^String your-name] (str "hello, " your-name))
これが今回のコンパイルエラーの原因です。:dynamicという名前のクラスを探して失敗していたのです。
これからのメタデータ
そして、1.3では:dynamic以外にもtrueかどうかをコンパイラが調べるメタデータが追加されたので、^:keywordが^{:keyword true}に展開されるようになりました。
最後に、耳当てつきのvarはダイナミックスコープを持つと自動的にみなされない - OGINO Masanori@はてなの最後のコードを1.2と1.3の両方で動かす方法を示します。
一つは、^{:dynamic true}と書くことです。1.2のコンパイラは:dynamicを考慮しませんが、害もありません。
(def ^{:dynamic true} *fred*)
1.3へ完全に移行してから^:dynamicに書き直してもいいですし、書き直さなくても特に問題はありません。
もう一つは、1.2のコンパイラが:tagメタデータを最初の一つだけ読み込む点を利用して、先に型を指定することです。
;; *fred*は文字列 (def ^String ^:dynamic *fred*)
しかし、少々トリッキーなので、私はあまり好みません。
おわりに
私の検証不足で人に誤りを伝えました。申し訳ありません。