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)

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

    用户> `'~(解析 'x)
    '#'用户/x
我很好奇……在您展示的原始示例中,您展示了手动准引用和拼接……
在最后的示例中,您只有没有准引用的正常形式。  推测,这些表达式是在某些词法环境中组合和 eval 的。

如果意图是捕获代码,为什么没有

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

足够?  是否有某种分析级别(例如,覆盖变量)是我没有看到,您打算包含在“调用”中的?

如果您想捕获源代码并对其进行评估,将源代码和结果保存在某些结构中,那么使用我的建议仍然是可行的。   类似如下:

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

我缺少什么?
是的,您是对的。我把我的例子简化得太过了。事实上,我在闭包中使用了一些 let-bindings、方法参数以及 also 变量。


    (let [n 1
          foo (调用 n '(foo bar) (+ n 2))]
      (.代码 foo))

问题是,n 在您的示例中是符号,但我想让它是实际值。
嗯。看起来你需要在这样的隐式近似引号中提供关于词汇环境的信息。这就是为什么我提到了使用反引号的原因;因为近似引号和相关拼接操作在clojure本身中并未公开(读取器会为你推断它们)。所以你需要刮取代码并注入等效的代码......或者也许有一个简单的规则可以直接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内部的函数。所以我觉得我还是暂时坚持我自己的手动引号/取消引号。我还尝试了使用反引号做同样的事情,但那抛出了几个空指针。

感谢您的帮助!
...