首页 > 解决方案 > 为什么在 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 宏的帖子中解释的模型时。

标签: 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,而不尝试其他特殊形式(如doletif)或具有预先存在的函数(如println. 如果您想进一步探索,您可能应该在 Clojure 电子邮件列表中查询:

clojure@googlegroups.com 

上面的代码就是基于这个模板项目


推荐阅读