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

欢迎!关于这个工作的更多信息,请查看关于页面。

0

我有一个手动引用代码的函数。
用法如下

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

但我不记得总是使用反引号(`)来标记sexp,并且要取消quote所有在环境中使用的符号。

使用宏来完成这个工作是什么样的?我到目前为止有了这个:

(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)

语法quote、quote、unquote部分的作用是什么?
这将生成一个引用符号,该符号已在当前命名空间中解析。

    user> `'~(resolve 'x)
    '#'user/x
我很好奇……在您展示的原例中,您展示了手动准引用和拼接……
在最后的例子中,只有普通形式,没有准引用。  假设表达式是在某种词法环境中组合并求值的。

如果目的是捕获代码,为什么,(defmacro call)

    (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
                   (? [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的内部方法。所以我认为我暂时会坚持手动的引用/非引用。我还尝试了同样的事情与反引号,但是它抛出了几个空指针。

感谢您的帮助!
...