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

欢迎!请参阅关于页面以了解更多此工作的信息。

0

我有一个函数用于捕获代码,我手动引用代码。
用法如下

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

但我总是忘记加引导符(backtick)和反引用环境中所有的符号。

有哪些方法可以用宏来实现这一点?我目前想到了这个

(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? 表达式) `'~(解析 表达式)

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

    user> `'~(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 实际中并未公开(读取器代表您推断它们)。所以你可能需要刮取代码并注入等效代码……或者可能有一条简单的规则,即在形式未引用时直接评估变量。嗯
我终于有了这个

    (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])
      [n 1
      ^io.github.fp7.statem.Call foo (call n '(foo bar) (+ n 2) p)]
      (.code foo))

    (test "")

它工作了,但是调用了 clojure 的内部方法。所以我想我会暂时坚持自己手动引号/反引号。我也尝试了相同的反引号方法,但是出现了一些空指针异常。

谢谢你的帮助!
...