请在 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
作者
我很好奇……在你最初的例子中,你展示了手动准引用和拼接……
在最后的例子中,你只有没有准引用的正常形式。  假设表达式是在某个词法环境中组合并评估的。

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

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

足够吗?  是否有我想包含在“call”中的分析级别(例如封闭变量)我看不到?

如果您想捕获源代码并对其进行评估,将源代码和结果保存到某种结构中,那么我的建议仍然可行。   比如  

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

我在错过什么?
作者
是的,你是正确的。我把我的例子简化得太多了。实际上,我正在封闭一些 let-bindings、方法参数和变量。


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

问题在于 n 是你的例子中的符号,但我想要它的实际值。
作者
嗯。似乎你需要关于词汇环境的信息来启用这种隐式伪引。这就是我提到使用反引号的原因;因为`quasiquote`和相关的拼接操作在clojure本体中并未暴露(读取器会代表你推断它们)。所以你需要 scrape 代码并注入等效代码......也许有一个简单的规则,如果不在 quoted 的形式中就 just eval 语句。嗯
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 的内部方法。所以我想我在短期内将坚持于我的 manual quoting/unquoting。我也用反引号试过同样的操作,但是出现了一些 null pointer。

谢谢你的帮助!
...