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)

语法报价、引用、取消引用这部分做什么?
它将产生一个引用符号,其中符号已在当前命名空间中解析。

    user> `'~(resolve 'x)
    '#'user/x
我在想……你最初展示的例子中,你展示了手动近似引用和拼接……
在最后的例子中,你没有使用近似引用,只有正常形式。  根据假设,表达式是在某个词法环境中组合并eval'd的。

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

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

就足够了?  我是不是没有看到你打算包含在"call"中的某个分析级别(例如,闭包变量)?

如果你想要捕获源代码并对其进行评估,将源代码和结果保存在某个结构中,那么仍然可以使用我的建议。   类似于  

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

我遗漏了什么?
是的,你没有错。我简化了我的例子太多了。实际上,我正在闭包一些let-binding,方法参数,以及一些变量。


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

问题在于,n是符号,但我想它应该是真实值。
嗯。看样子你需要关于词汇环境的资料才能实现这种隐式近引用。这就是我提到使用反引号的原因;因为近引用和相关拼接操作在clojure中并未暴露(阅读器会代表你推断它们)。所以你可能需要从代码中提取并注入等效代码......或者可能有一条简单的规则可以在形式未进行引用时评估变量。嗯
by
我终于有了这个

   (deftype Call [code])

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

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


     (.get f nil)))
     (def ^:private syntax-quote
     (let [m (.getDeclaredMethod clojure.lang.LispReader$SyntaxQuoteReader
     [  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
      [& body]
     [  clojure.lang.Var/popThreadBindings])))
     (defmacro call
     [  let [ks (set (keys &env))
     [  myvar (walk/postwalk
     [  clojure.core/unquote sexp)
     [  if (contains? ks 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的内部方法。所以我暂时还是会坚持我手动引用/反引用的方法。我也尝试了使用反引号做同样的事情,但那抛出了几个空指针异常。
...