概要
x
主にローカルは実行時にのみ割り当てられた値を取得するのに対し、タグ付きリテラルは読み取り時に処理されるため、例でローカルの値を取得する方法はありません。(その間にもコンパイル時間があります。コンパイル時にローカルの値を取得することは不可能です。したがって、マクロもローカルの値を取得できません。) 1
より良いアプローチは、実行時に通常の関数を使用することです。これは、結局のところ、一部のパラメーターの実行時の値に基づいて値を作成する必要があるためです。タグ付きリテラルは実際にはリテラルであり、そのように使用する必要があります。
拡張ディスカッション
上記の問題を説明するには、次のようにします。
(binding [*data-readers* {'bar (fn [_] (java.util.Date.))}]
(eval (read-string "(defn foo [] #bar x)")))
foo
は常に同じ値を返します。これは、リーダーが値を返す機会が 1 回しかなく、その値がのバイトコード#bar x
に焼き付けられるためです。foo
eval
また、直接渡す代わりに、呼び出しによって返されたデータ構造を保存read-string
し、将来の任意の時点でコンパイルできることにも注意してください。によって返される値はfoo
同じままです。明らかに、そのようなリテラル値がローカルの将来の値に依存する方法はありません。実際、リーダーの操作中には、どのシンボルがローカルに名前を付けるようになるかさえ明確ではありません。これはコンパイラーが決定することであり、その決定の結果は、マクロが関与する場合には明らかではない可能性があります。
もちろん、読者は関数呼び出しやローカルの名前などのように見えるフォームを自由に返すことができます。一例を示すには:
(binding [*data-readers* {'bar (fn [sym] (list sym 1 2 3 4 5))}]
(eval (read-string "#bar *")))
;= 120
;; substituting + for * in the string yields a value of 15
ここ#bar f
は と等価になり(f 1 2 3 4 5)
ます。言うまでもなく、これは表記法を乱用したものであり、実際には要求したことを実行しません。
1eval
はローカルにアクセスできない (常にグローバル スコープで動作する)ことは指摘しておく価値がありますが、実行前に値が割り当てられていないローカルの問題はより根本的なものです。