为什么Clojure堆栈跟踪这么久?

Wil*_*hes 33 clojure

我以前写了一些玩具解释器/编译器,所以我将堆栈跟踪引用编译器源文件中的行与编译器错误相关联.

如果我将以下错误的函数定义添加到我的compojure项目中:

(defn dodgy-map []
  {:1 :2 :3})
Run Code Online (Sandbox Code Playgroud)

Lein拒绝开始:

$ lein ring server-headless
Exception in thread "main" java.lang.RuntimeException: Map literal must contain an even number of forms, compiling:(one_man_wiki/views.clj:19)
        at clojure.lang.Compiler.load(Compiler.java:6958)
        at clojure.lang.RT.loadResourceScript(RT.java:359)
        at clojure.lang.RT.loadResourceScript(RT.java:350)
        at clojure.lang.RT.load(RT.java:429)
        at clojure.lang.RT.load(RT.java:400)
        at clojure.core$load$fn__4890.invoke(core.clj:5415)
        at clojure.core$load.doInvoke(core.clj:5414)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invoke(core.clj:5227)
        at clojure.core$load_lib.doInvoke(core.clj:5264)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invoke(core.clj:603)
        at clojure.core$load_libs.doInvoke(core.clj:5298)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invoke(core.clj:603)
        at clojure.core$require.doInvoke(core.clj:5381)
        at clojure.lang.RestFn.invoke(RestFn.java:457)
        at one_man_wiki.handler$eval1564$loading__4784__auto____1565.invoke(handler.clj:1)
        at one_man_wiki.handler$eval1564.invoke(handler.clj:1)
        at clojure.lang.Compiler.eval(Compiler.java:6511)
        at clojure.lang.Compiler.eval(Compiler.java:6501)
        at clojure.lang.Compiler.load(Compiler.java:6952)
        at clojure.lang.RT.loadResourceScript(RT.java:359)
        at clojure.lang.RT.loadResourceScript(RT.java:350)
        at clojure.lang.RT.load(RT.java:429)
        at clojure.lang.RT.load(RT.java:400)
        at clojure.core$load$fn__4890.invoke(core.clj:5415)
        at clojure.core$load.doInvoke(core.clj:5414)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invoke(core.clj:5227)
        at clojure.core$load_lib.doInvoke(core.clj:5264)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invoke(core.clj:603)
        at clojure.core$load_libs.doInvoke(core.clj:5298)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invoke(core.clj:603)
        at clojure.core$require.doInvoke(core.clj:5381)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at user$eval1.invoke(NO_SOURCE_FILE:1)
        at clojure.lang.Compiler.eval(Compiler.java:6511)
        at clojure.lang.Compiler.eval(Compiler.java:6500)
        at clojure.lang.Compiler.eval(Compiler.java:6477)
        at clojure.core$eval.invoke(core.clj:2797)
        at clojure.main$eval_opt.invoke(main.clj:297)
        at clojure.main$initialize.invoke(main.clj:316)
        at clojure.main$null_opt.invoke(main.clj:349)
        at clojure.main$main.doInvoke(main.clj:427)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at clojure.lang.Var.invoke(Var.java:419)
        at clojure.lang.AFn.applyToHelper(AFn.java:163)
        at clojure.lang.Var.applyTo(Var.java:532)
        at clojure.main.main(main.java:37)
Caused by: java.lang.RuntimeException: Map literal must contain an even number of forms
        at clojure.lang.Util.runtimeException(Util.java:170)
        at clojure.lang.LispReader$MapReader.invoke(LispReader.java:1071)
        at clojure.lang.LispReader.readDelimitedList(LispReader.java:1126)
        at clojure.lang.LispReader$ListReader.invoke(LispReader.java:962)
        at clojure.lang.LispReader.read(LispReader.java:180)
        at clojure.lang.Compiler.load(Compiler.java:6949)
        ... 51 more
Run Code Online (Sandbox Code Playgroud)

如果我引用不存在的变量:

(defn no-such-variable []
  i-dont-exist)
Run Code Online (Sandbox Code Playgroud)

我得到了一个同样巨大的追溯:

Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: i-dont-exist in this context, compiling:(one_man_wiki/views.clj:18)
        at clojure.lang.Compiler.analyze(Compiler.java:6281)
        at clojure.lang.Compiler.analyze(Compiler.java:6223)
        at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618)
        at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5054)
        at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3674)
        at clojure.lang.Compiler.analyzeSeq(Compiler.java:6453)
        at clojure.lang.Compiler.analyze(Compiler.java:6262)
        at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
        at clojure.lang.Compiler.analyze(Compiler.java:6262)
        at clojure.lang.Compiler.access$100(Compiler.java:37)
        at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:518)
        at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
        at clojure.lang.Compiler.analyze(Compiler.java:6262)
        at clojure.lang.Compiler.analyze(Compiler.java:6223)
        at clojure.lang.Compiler.eval(Compiler.java:6515)
        at clojure.lang.Compiler.load(Compiler.java:6952)
        at clojure.lang.RT.loadResourceScript(RT.java:359)
        at clojure.lang.RT.loadResourceScript(RT.java:350)
        at clojure.lang.RT.load(RT.java:429)
        at clojure.lang.RT.load(RT.java:400)
        at clojure.core$load$fn__4890.invoke(core.clj:5415)
        at clojure.core$load.doInvoke(core.clj:5414)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invoke(core.clj:5227)
        at clojure.core$load_lib.doInvoke(core.clj:5264)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invoke(core.clj:603)
        at clojure.core$load_libs.doInvoke(core.clj:5298)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invoke(core.clj:603)
        at clojure.core$require.doInvoke(core.clj:5381)
        at clojure.lang.RestFn.invoke(RestFn.java:457)
        at one_man_wiki.handler$eval1564$loading__4784__auto____1565.invoke(handler.clj:1)
        at one_man_wiki.handler$eval1564.invoke(handler.clj:1)
        at clojure.lang.Compiler.eval(Compiler.java:6511)
        at clojure.lang.Compiler.eval(Compiler.java:6501)
        at clojure.lang.Compiler.load(Compiler.java:6952)
        at clojure.lang.RT.loadResourceScript(RT.java:359)
        at clojure.lang.RT.loadResourceScript(RT.java:350)
        at clojure.lang.RT.load(RT.java:429)
        at clojure.lang.RT.load(RT.java:400)
        at clojure.core$load$fn__4890.invoke(core.clj:5415)
        at clojure.core$load.doInvoke(core.clj:5414)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invoke(core.clj:5227)
        at clojure.core$load_lib.doInvoke(core.clj:5264)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invoke(core.clj:603)
        at clojure.core$load_libs.doInvoke(core.clj:5298)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invoke(core.clj:603)
        at clojure.core$require.doInvoke(core.clj:5381)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at user$eval1.invoke(NO_SOURCE_FILE:1)
        at clojure.lang.Compiler.eval(Compiler.java:6511)
        at clojure.lang.Compiler.eval(Compiler.java:6500)
        at clojure.lang.Compiler.eval(Compiler.java:6477)
        at clojure.core$eval.invoke(core.clj:2797)
        at clojure.main$eval_opt.invoke(main.clj:297)
        at clojure.main$initialize.invoke(main.clj:316)
        at clojure.main$null_opt.invoke(main.clj:349)
        at clojure.main$main.doInvoke(main.clj:427)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at clojure.lang.Var.invoke(Var.java:419)
        at clojure.lang.AFn.applyToHelper(AFn.java:163)
        at clojure.lang.Var.applyTo(Var.java:532)
        at clojure.main.main(main.java:37)
Caused by: java.lang.RuntimeException: Unable to resolve symbol: i-dont-exist in this context
        at clojure.lang.Util.runtimeException(Util.java:170)
        at clojure.lang.Compiler.resolveIn(Compiler.java:6766)
        at clojure.lang.Compiler.resolve(Compiler.java:6710)
        at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6671)
        at clojure.lang.Compiler.analyze(Compiler.java:6244)
        ... 66 more
Run Code Online (Sandbox Code Playgroud)

为什么Clojure编译器没有引发a ClojureSyntaxError和a ClojureNameError可以在顶层捕获并显示一个简单的错误?这些是开发过程中常见的开发人员错

如果长回溯在某些情况下有用,为什么它们会被截断?

编辑:我在回答中寻找的内容:

  1. 在获取引用clojure.lang的回溯是否有用时,是否存在某种情况(某些元编程与Java互操作?)?
  2. (相关)是否有任何技术限制阻止添加ClojureSyntaxError如上所述的?是否值得我在Clojure bug跟踪器上打开一个问题?(更新:我已经开了CLJ-1155)
  3. 如何体验Clojure程序员阅读这些追溯?有工具可以提供帮助吗?每个人都使用clj-stacktrace吗?

Joh*_*hnJ 14

回复你的编号点,

  1. 在Clojure中直接与Java库进行互操作是惯用的,因此如果以某种意外或不支持的方式调用Java对象,获取完整的Java堆栈跟踪会很有帮助.

  2. 听起来是一个好主意; 我经常至少希望有一个可设置的选项,它允许我只看到源自我自己的代码的堆栈跟踪部分,从而抑制所有底层语言层.

  3. 我通常只是努力工作并在堆栈跟踪上进行操作以获得我的程序中的行,这些行进行了调整,调整了clojure.*部分(我通常会测试每一分钟的变化,因此我非常清楚导致问题的变化).我使用的一些Emacs和Eclipse工具只显示实际错误,而不是整个堆栈跟踪; 我通常认为这更有帮助.

    在2012年的Clojure/west,@ chouser就堆叠痕迹的解剖进行了很好的讨论 [PDF],解释了如何阅读它们,并介绍了一个看起来很有前途的工具,显然还没有看到光明的一天.

与Python相比,我认为它的堆栈跟踪非常友好,我认为堆栈跟踪在Clojure中是一个粗略的优势,特别是对于初学者而言.这部分是由于语言的"托管"性质,尽管我预计可以在不增加偶然复杂性的情况下进行改进.