Clojure 为什么dotrace在这里抛出堆栈溢出错误? 简言之:
这是因为Clojure 为什么dotrace在这里抛出堆栈溢出错误? 简言之:,clojure,Clojure,这是因为trace fn call,这是dotrace用来包装要跟踪的函数的东西,它使用str生成nicetrace foo=>val输出 扩展解释: dotrace宏通过为每个Var安装一个线程绑定来实现它的魔力,该Var持有一个要跟踪的函数;在本例中,有一个这样的变量,clojure.core/str。替代方案大致如下: (use '[clojure.contrib.trace]) (dotrace [str] (reduce str [\a \b])) 用docstring的话来说,tr
trace fn call
,这是dotrace
用来包装要跟踪的函数的东西,它使用str
生成nicetrace foo=>val
输出
扩展解释:
dotrace
宏通过为每个Var安装一个线程绑定来实现它的魔力,该Var持有一个要跟踪的函数;在本例中,有一个这样的变量,clojure.core/str
。替代方案大致如下:
(use '[clojure.contrib.trace])
(dotrace [str] (reduce str [\a \b]))
用docstring的话来说,trace fn调用“用args跟踪对函数f的单个调用”。在执行此操作时,它调用跟踪函数,记录返回值,打印一条格式为TRACE foo=>val
的信息性消息,并返回从跟踪函数获得的值,以便可以继续正常执行
如上所述,此TRACE foo=>val
消息是使用str
生成的;然而,在本例中,这实际上是被跟踪的函数,因此对它的调用导致对trace fn call
的另一个调用,该调用尝试使用str
生成跟踪输出字符串,从而导致对trace fn call
的另一个调用。。。最终导致烟囱爆炸
解决办法:
以下修改版本的dotrace
和trace fn call
即使在核心变量存在奇怪绑定的情况下也可以正常工作(请注意,未来可能无法及时安排;如果出现问题,请参阅下文):
(围绕常规的dotrace
重新绑定trace fn调用显然不起作用;我猜这是因为clojure.
Var调用仍然是由编译器硬连接的,但这是另一回事。无论如何,上述操作都会起作用。)
另一种方法是将上述my dotrace
宏与不使用期货的my trace fn call
函数一起使用,但修改为使用以下函数代替str
调用clojure.contrib.trace
函数的自定义替换:
(defn my-trace-fn-call
"Traces a single call to a function f with args. 'name' is the
symbol name of the function."
[name f args]
(let [id (gensym "t")]
@(future (tracer id (str (trace-indent) (pr-str (cons name args)))))
(let [value (binding [*trace-depth* (inc *trace-depth*)]
(apply f args))]
@(future (tracer id (str (trace-indent) "=> " (pr-str value))))
value)))
(defmacro my-dotrace
"Given a sequence of function identifiers, evaluate the body
expressions in an environment in which the identifiers are bound to
the traced functions. Does not work on inlined functions,
such as clojure.core/+"
[fnames & exprs]
`(binding [~@(interleave fnames
(for [fname fnames]
`(let [f# @(var ~fname)]
(fn [& args#]
(my-trace-fn-call '~fname f# args#)))))]
~@exprs))
替换是简单而乏味的,我在答案中省略了它们。这是否应该被视为一个bug,应该在哪里报告?或者我应该将str重新绑定到smth以实现这一点?这似乎至少是一个文档错误,尽管我有一些想法,可以对c.c.trace
进行非颠覆性的修改,这将使它在面临核心变量反弹时更加健壮。。。我可能会考虑测试一些设计。目前,我已经在一个变通方案中进行了编辑,希望您能接受。我担心,直接重新绑定任何东西都是行不通的。
(defn my-trace-fn-call
"Traces a single call to a function f with args. 'name' is the
symbol name of the function."
[name f args]
(let [id (gensym "t")]
@(future (tracer id (str (trace-indent) (pr-str (cons name args)))))
(let [value (binding [*trace-depth* (inc *trace-depth*)]
(apply f args))]
@(future (tracer id (str (trace-indent) "=> " (pr-str value))))
value)))
(defmacro my-dotrace
"Given a sequence of function identifiers, evaluate the body
expressions in an environment in which the identifiers are bound to
the traced functions. Does not work on inlined functions,
such as clojure.core/+"
[fnames & exprs]
`(binding [~@(interleave fnames
(for [fname fnames]
`(let [f# @(var ~fname)]
(fn [& args#]
(my-trace-fn-call '~fname f# args#)))))]
~@exprs))
(defn my-str [& args] (apply (.getRoot #'clojure.core/str) args))