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)

语法引号、引号和解引号部分的作用是什么?
将返回一个引用符号,其中符号已在当前命名空间中解析。

用户》'~(resolve 'x)
'#'user/x
我很好奇……您最初展示的示例中使用了手动近似引用和拼接……
在最后的示例中,您只有正常形式的表达式,没有使用近似引用。  假设这些表达式会在某个词法环境中组合和评估。

如果要捕获代码,那么为什么不使用

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

足够?  我是不是没看到您打算包括在“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的内部方法。  因此我认为我暂时还会坚持我的手动引用/取消引用。我还用反引号尝试了同样的方法,但它抛出了一些空指针。

谢谢你的帮助!
...