评估Clojure标记文字的参数

时间:2013-11-09 21:15:10

标签: macros clojure literals

我一直在尝试使用Clojure标记的文字,并注意到读者不会评估参数,非常像宏。这是有道理的,但这样做的适当解决方案是什么?明确的评估?

示例:给定此功能

(defn my-data
  ([[arg]]
     (prn (symbol? arg))
     :ok))

和此定义data_readers.clj

{myns/my-data my.ns/my-data}

以下表现不同:

> (let [x 1] (my.ns/my-data [x]))
false
:ok

因此传入的x在传递到my-data之前会被评估。另一方面:

> (let [x 1] #myns/my-data [x])
true
:ok

因此,如果我想在x内使用my-data的值,则my-data函数需要对其执行某些操作,即检查x是否为符号,若然,请使用(eval x)。这看起来很难看。有更好的方法吗?

1 个答案:

答案 0 :(得分:3)

摘要

在您的示例中无法获取本地x的值,主要是因为locals仅在运行时获取指定的值,而标记的文本在读取时处理。 (两者之间也有编译时间;在编译时不可能得到本地的值;因此宏也不能得到本地的值。) 1

更好的方法是在运行时使用常规函数,因为毕竟你想根据某些参数的运行时值构造一个值。标记的文字实际上是文字,应该这样使用。

扩展讨论

为了说明上述问题:

(binding [*data-readers* {'bar (fn [_] (java.util.Date.))}]
  (eval (read-string "(defn foo [] #bar x)")))

foo将始终返回相同的值,因为读者只有一次机会返回#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)。毋庸置疑,这是对符号的滥用,并没有真正按照你的要求行事。


1 值得指出的是eval无法访问本地(它总是在全局范围内运行),但本地人没有在运行时分配值的问题更为基础