clojure - 为什么在 Clojure 中将元数据添加到函数定义与其他形式的工作方式不同?
问题描述
为什么会失败:
(eval (with-meta '(fn [] 0) {:stack (gensym "overflow")}))
; Syntax error compiling at (REPL:1:1).
; Unable to resolve symbol: overflow210 in this context
当以下都没有失败时?
(eval (with-meta '(do [] 0) {:stack (gensym "overflow")}))
; 0
(eval (with-meta '(let [] 0) {:stack (gensym "overflow")}))
; 0
(eval (with-meta '(if true 0 1) {:stack (gensym "overflow")}))
; 0
(eval (with-meta '(println "hello") {:stack (gensym "overflow")}))
; hello
; nil
上面的例子是我试图找到一个最小的、可重现的例子。我在处理宏时遇到了这个问题,这里有一个简化的例子:
(defmacro my-macro []
(with-meta '(fn [] 0) {:stack (gensym "overflow")}))
(my-macro)
; Syntax error compiling at (REPL:1:1).
; Unable to resolve symbol: overflow156 in this context
在尝试遵循这篇关于测试 Clojure 宏的帖子中解释的模型时。
解决方案
问题是您对 的使用gensym
,而不是元数据。观察:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(dotest
(binding [*print-meta* true]
(let [
fn-code-plain '(fn [] 42)
fn-code-meta (with-meta '(fn [] 42) {:alpha true})
fn-code-sym-meta (with-meta '(fn [] 42) {:alpha (gensym "dummy")})
]
(prn fn-code-plain)
(prn fn-code-meta)
\
(prn (eval fn-code-plain))
(prn (eval fn-code-meta))
(newline)
(prn fn-code-sym-meta)
(throws? (eval fn-code-sym-meta))
)
))
结果:
^{:line 7, :column 27} (fn [] 42)
^{:alpha true} (fn [] 42)
#object[user$eval19866$fn__19867 0xac891b5 "user$eval19866$fn__19867@ac891b5"]
^{:alpha true} #object[user$eval19870$fn__19871 0x2d1ab7e0 "user$eval19870$fn__19871@2d1ab7e0"]
^{:alpha dummy19865} (fn [] 42)
问题是eval
看到符号dummy19865
并试图解决它。它是由创建的这一事实gensym
是无关紧要的。关键字没问题:
fn-code-kw-meta (with-meta '(fn [] 42) {:alpha :dummy-42})
<snip>
(prn fn-code-kw-meta)
(prn (eval fn-code-kw-meta))
生产:
^{:alpha :dummy-42} (fn [] 42)
^{:alpha :dummy-42} #object[user$eval19953$fn__19954 0x13253ac7 "user$eval19953$fn__19954@13253ac7"]
或定义的符号:
(def mysym "Forty-Two!")
<snip>
fn-code-mysym-meta (with-meta '(fn [] 42) {:alpha mysym})
<snip>
(prn fn-code-mysym-meta)
(prn (eval fn-code-mysym-meta))
结果:
^{:alpha "Forty-Two!"} (fn [] 42)
^{:alpha "Forty-Two!"} #object[user$eval20082$fn__20083 0x24a63de5 "user$eval20082$fn__20083@24a63de5"]
概括:
您已经演示了eval
仅尝试对表单的元数据进行符号解析fn
,而不尝试其他特殊形式(如do
、let
、if
)或具有预先存在的函数(如println
. 如果您想进一步探索,您可能应该在 Clojure 电子邮件列表中查询:
clojure@googlegroups.com
上面的代码就是基于这个模板项目。
推荐阅读
- twitter - 如何避免 Twitter API 的 Like Tweet 端点中的 404 错误?
- javascript - 在这个挑战中,我如何遵循 DRY 原则?
- javascript - Jquery双击不适用于动态div
- c# - 由于某些奇怪的原因,解析 int 不起作用
- python-3.x - 网页抓取时我们将“html.parser”参数放在哪里?
- python - Pandas 系列直方图 - 如何使子图显示在单独的图表上?
- python - 当我尝试运行 Odoo 时,为什么会出现错误“名称节点不能与 'None' 常量一起使用”?
- reactjs - 用苹果登录并做出反应
- node.js - 在将其内容保存到 multer 之前提取 ZIP 文件
- c - 在 C 中格式化浮点输入