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

我的第一个想法是,[a rel="nofollow" href="https://github.com/brandonbloom/backtick" target="_blank">backtick

谢谢。我查看了它,有些我不明白的东西,例如。

        (symbol? form) `'~(resolve form)

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

    user> `'~(resolve 'x)
    '#'user/x
我很好奇……在你最初显示的例子中,你展示了手动约简和拼接。
在最后的例子中,你只有没有约简的正常形式。  假设,表达式是在某个词法环境中组合和评估的。

如果打算捕获代码,为什么不是

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

sufficient?  是否有我看不到的分析层级(例如,封装变元)你想包括在 "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 中公开(读取器会为你推断它们)。所以你需要抓取代码并注入等价的代码......或者可能有一个简单的规则,如果形式未被引用,只需评估其中的变量。嗯
by
我终于有了这个

    (deftype Call [code])

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

    (def ^:private gen-sym-var
      (let [f (.getDeclaredField clojure.lang.LispReader "GENSYM_ENV")]
                                  "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)

      (let [ks (set (keys &env))
      [& body]
            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 的内部方法。所以我认为我暂时会坚持手动引用/取消引用。我也尝试了同样的方法用反引号,但是它抛了一些空指针异常。

谢谢你的帮助!

欢迎来到 Clojure Q&A,您可以在那里向 Clojure 社区成员提问并获得答案。
...