2024 年 Clojure 状况调查! 中分享您的想法。

欢迎!请查看 关于 页面以获取更多关于如何使用本站的信息。

0

我有一个函数用于捕捉代码,我必须手动输入代码。
用法看起来像这样

(statem/lazy-call `(page->csrf-token ~r))

但我总是忘记为 sexp 背对空格和发行出现在环境中的所有符号。

如何使用宏来完成这项工作?我到目前为止想出的是这个

(deftype Call [code])

(defn call*
  [code]
  (Call. code))

(defmacro call
  [& body]
  `(call*
    ~(walk/postwalk
      (fn [d]
        (cond
          (instance? clojure.lang.IPersistentList d) `(list ~@d)
          :else  d))
      body)))

(let [foo (call (println "bar") '(foo bar) (+ 1 2))]
  (println (.code foo)))

但如果在体内或其它特殊形式中存在引用列表,则它不会起作用。

1 个回答

+1

我的首要想法是 backtick 会有一点点帮助。这是个很有趣的问题。

谢谢。我查看了这些内容。有我不太懂的部分,比如。

        (symbol? form) `'~(resolve form)

语法引用(syntax-quote),引用(quote)和取消引用(unquote)部分的作用是什么?
将产生一个引用符号,该符号已在当前命名空间中解析。

    user> `'~(resolve 'x)
    '#'user/x
我很好奇……你在原始示例中展示了手动创建近似引号和拼接……
在最终示例中,你只有正常的表单,没有近似引号。  假设表达式是在某些字面环境中进行组合和评估的。

如果目的是捕获代码,为什么不是

    (defmacro call
      [& body]
      `(call* '~body))

sufficient?  是因为我没有看到你打算在“call”中包括某种程度的分析(例如,闭包变量)?

如果你想要捕获源代码并评估它,将源代码和结果保存在某种结构中,则我的建议仍然可行。   例如,  

    (deftype Call [code]
      clojure.lang.IFn
      (invoke [this] (eval `(do ~code))))  ;; 可能会缓存结果,这是一个示例。

我遗漏了什么?
是的,你没有错。我简化了我的例子太多。我实际上闭包了一些let绑 定、方法参数和变量。


    (let [n 1
          foo (call n '(foo bar) (+ n 2))]
      (.code foo))

问题在于n是符号,但你希望它是实际值。
嗯。看起来你需要关于词法环境的信息,以便启用这种隐式伪引用。这就是我提到使用反引号的原因;因为`quasiquote`和相关拼接操作在Clojure中并没有暴露(读取器代表你推断它们)。所以你需要刮取代码并注入等效代码......或者可能有一个简单的规则,如果形式未引用,则直接评估变量。嗯
by
我终于有了这个

    (deftype Call [code])

    (defn call*)
      [code]
      (Call. code))

    (def ^:private gen-sym-var
      (let [f (.getDeclaredField clojure.lang.LispReader
                                  "GENSYM_ENV")]
        (.setAccessible f true)
        (.get f nil)))


    (def ^:private syntax-quote
      (let [m (.getDeclaredMethod clojure.lang.LispReader$SyntaxQuoteReader
                                  "syntaxQuote" (into-array Class [Object]))]
        (.setAccessible m true)
        (fn [form]
          (try
            (clojure.lang.Var/pushThreadBindings (hash-map gen-sym-var {}))
            (.invoke m nil (into-array Object [form]))
            (finally
              (clojure.lang.Var/popThreadBindings))))))

    (defmacro  call
      [& body]
      (let [ks (set (keys &env))
            myvar (walk/postwalk
                   (fn [sexp]
                     (if (contains? ks sexp)
                       (list 'clojure.core/unquote sexp)
                       sexp))
                   body)]
        `(call* ~(syntax-quote myvar))))

    (defn test
      [p]
      (let [n 1
            ^io.github.fp7.statem.Call foo (call n '(foo bar) (+ n 2) p)]
        (.code foo)))

    (test "")

它按预期工作,但调用了Clojure的内部方法。所以我认为我暂时还是会坚持手动引用/取消引用。我还尝试了反引号的方法,但出现了空指针错误。

谢谢你的帮助!
...