clojure - 在宏中使用 eval(全局变量意外解析)

标签 clojure macros

最近我发现在宏中使用 eval,我知道这有点失礼,但我们暂时忽略它。让我感到惊讶的是,eval 能够在宏扩展时解析全局变量。下面是一个人为的例子,只是为了说明我所指的情况:

(def list-of-things (range 10))

(defmacro force-eval [args]
  (apply + (eval args)))

(macroexpand-1 '(force-eval list-of-things))

; => 45

我希望 args 解析为 force-eval 中的符号 list-of-things,然后是 list -of-things 被评估导致错误,因为它是未绑定(bind)的:

“在此上下文中无法解析符号事物列表”

但是,list-of-things 被解析为 (range 10) 并且没有抛出错误 - 宏扩展成功。

将此与尝试执行相同的宏扩展进行对比,但在本地绑定(bind)上下文中:

(defmacro force-eval [args]
  (apply + (eval args)))

(let [list-of-things (range 10)]
  (macroexpand-1 '(force-eval list-of-things)))

; => Unable to resolve symbol: list-of-thingss in this context

请注意,在上面的示例中,我假设 list-of-things 之前未绑定(bind),例如一个新的 REPL。最后一个例子说明了为什么这很重要:

(defmacro force-eval [args]
  (apply + (eval args)))

(def list-of-things (range 10 20))

(let [list-of-thing (range 10)]
  (macroexpand-1 '(force-eval list-of-things)))

; => 145

上面的例子表明局部变量被忽略了,这是 eval 的预期行为,但是当您期望全局变量在宏扩展时也不可用时会有点困惑。

我似乎对宏展开时究竟可用什么有误解。我以前认为基本上任何绑定(bind),无论是全局的还是本地的,在运行时之前都是不可用的。显然这是一个错误的假设。 我的困惑的答案仅仅是全局变量在宏扩展时可用吗?还是我在这里遗漏了一些细微差别?

注意:this related post closely 描述了一个类似的问题,但重点更多地放在如何避免不恰本地使用 eval 上。我主要想了解为什么 eval 在第一个示例中起作用,以及扩展在宏扩展时 eval 可用的内容。

最佳答案

当然,vars 必须在编译时可见。这就是存储 first+ 等函数的地方。没有它们,您将无能为力。

但请记住,您必须确保正确引用它们。在 repl 中,*ns* 将被绑定(bind),因此对符号的引用将在当前命名空间中查找。如果您通过 -main 而不是 repl 运行程序,*ns* 将不会被绑定(bind),并且只会找到适当限定的变量。您可以确保使用

`(force-eval list-of-things)

代替

'(force-eval list-of-things)

注意我不区分全局变量和非全局变量。 Clojure 中的所有变量都是全局变量。本地绑定(bind)不称为变量。它们被称为局部变量、绑定(bind)、变量或这些词的某种组合。

关于clojure - 在宏中使用 eval(全局变量意外解析),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68433896/

相关文章:

emacs - 史莱姆模式错误

namespaces - 无法让 pprint 在 clojure 中工作

Clojure CLR - 实现包含属性的接口(interface)

macros - 在通用 lisp 宏中扩展参数列表

excel - 如果在 Excel 中满足条件,如何在两个单元格之间绘制虚线

c - 如何在 c 中清除此 lint 警告?

clojure - 如何指定lazy-seq生成函数?

Clojurescript 命名空间刷新

ios - 使用 ##__VA_ARGS__ 时 Xcode 编译器错误

java - Atlassian汇合: Macro type plugin that will display a popup window when the page is accessed