【发布时间】:2021-09-26 17:27:57
【问题描述】:
最近我在宏中遇到了 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 进行求值,从而导致错误,因为它未绑定:
"unable to resolve symbol list-of-things in this context"
但是,list-of-things 被解析为 (range 10) 并且没有引发错误 - 宏扩展成功。
将此与尝试执行相同的宏扩展进行对比,但在本地绑定上下文中:
(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 以前未绑定,例如一个新鲜的 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 的预期行为,但是当您期望全局变量在宏展开时也不可用时,这有点令人困惑。
我似乎对宏扩展时可用的确切内容存在误解。我以前曾认为,基本上任何绑定,无论是全局的还是本地的,都在运行时才可用。显然这是一个不正确的假设。 我的困惑的答案仅仅是全局变量在宏扩展时可用吗?还是我在这里遗漏了一些细微差别?
注意:this related post 详细描述了一个类似的问题,但重点更多地在于如何避免不当使用 eval。我主要想了解为什么eval 在第一个示例中起作用,以及扩展在宏扩展时可用于 eval 的内容。
【问题讨论】: