请在<än href="https://www.surveymonkey.com/r/clojure2024">2024年度Clojure调查!中分享您的看法。

欢迎!请参阅关于页面以获取更多关于如何使用本站的信息。

+2
错误

Clojure堆栈跟踪通常难以理解,难以追踪,是否有库可以显示更好的错误或格式化堆栈跟踪?

2个回答

+1

有多个库可用以修改打印堆栈跟踪的方式,提供如折叠、clojure帧和着色等功能。我们目前没有计划在下一个Clojure版本中对此进行修复工作。

你能指出这些库是哪一个吗?
Michel,如果您还没有使用 Clojure 1.10.1,我建议您升级。在 1.10 中进行了大量的错误信息工作,并在 1.10.1 中增加了更好的报告机制。

我还会附带一个链接到 Stuart Halloway 关于堆栈跟踪的 Twitter 线程:https://twitter.com/stuarthalloway/status/1148295437448876032

当您开始使用 Clojure 时,堆栈跟踪可能会让人感到害怕,但学会阅读它们是值得努力的。坦白说,当隐藏这些库时,折叠或省略它们并不像您想象的那么有帮助(因为有时重要信息在它们隐藏的片段中)。
当我听说更好的错误信息时,这是我在 1.10 中认为会发生的事情。
https://reasonml.github.io/blog/2017/08/25/way-nicer-error-messages.html

对于新手来说,像这样的错误“ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn”是模糊不清的,请参阅: https://stackoverflow.com/questions/26720847/classcastexception-java-lang-long-cannot-be-cast-to-clojure-lang-ifn

对我来说,这里的价值在于能够指向用户源代码中错误的列和行,以便快速显示错误在嵌套结构的何处。

然后更好地以 Clojure 的术语而不是 Java 的术语解释错误发生的原因。我并不想听到关于类的东西,因为在我的代码中(1 2 3),在我看来,没有类。

话虽如此,我对这个可能性非常天真。我们有这个语言的重度互操作性,这可能会使它比 elms 这样的封闭系统更难以实现。
柯华(Clojure)_可以检测_这些条件并抛出更明确的异常,但这会给每个人带来性能开销。历史上的选择总是以牺牲一些“不寻常”的Java异常来换取速度 — 因为每个新手只需要学习这些“一次”,所以这对于绝大多数用户来说是一个很好的权衡。

话虽如此,如果能有一种内置的“友好”的REPL选项,使用:caught提供更友好的错误信息,那将非常好。
例如

user=> (defn 轻松 [t]
                (clojure.main/repl-caught
                    (if (re-find #"无法转换成clojure.lang.IFn" (ex-message t))
                        (ex-info (str "预期是一个函数 -- 发现: " (second (re-find #"^([^ ]*)" (ex-message t)))) {:原因 t})
                    t)))
#'user/轻松
user=> (clojure.main/repl :caught 轻松)
user=> (1 2 3)
执行错误(ExceptionInfo)在user/轻松(REPL:1)。
预期是一个函数 -- 发现: java.lang.Long
user=>
+1

这里又对更好的默认错误消息投了一票。

下面提到的库在更多或更少的程度上成功地处理了症状,但让我们从一个想要学习Clojure的人的角度来看,他们会前往clojure.org,点击“入门”。

好吧,我现在知道如何下载它。也许我会尝试使用repl.it来感受一下。
现在我有了基本的经验(根据clojure.org,含糊不清的错误消息)(参见:“可能会看到的一个令人困惑的错误消息是意外将数据列表当作代码尝试进行评估的结果:”)

好吧,我们先去学习柯华(Clojure)。
好吧,仍然没有关于如何启动REPL的信息。
第三链接:启动REPL是个好主意。
第四链接:这里有启动REPL的n种方式。
这里是其他启动REPL的方法。

没有提到以下几点:如果你是初学者,让我们添加clj-stacktrace,然后启动REPL。
你看到为什么这不太实用了吗?

我知道你最近几年对更好的文档投入了大量精力,我对此表示抱歉。很难回到这样熟悉的事物,并以一个想要了解这种新语言的开发者的眼光来看待。

我相信你有一次或两次机会给人留下好印象,让人们感兴趣的可读错误消息告诉他们哪里出了问题,这是其中重要的一部分。

user=> (map 1 inc)
不知道如何从:clojure.core$inc创建ISeq。

user=> (1 2 3)
执行错误(ClassCastException)在user/eval7(REPL:1)。类java.lang.Long不能被转换为类clojure.lang.IFn(java.lang.Long位于模块java.base的加载器'bootstrap'中;clojure.lang.IFn位于未命名的模块的加载器'app'中)。

这两者都没有告诉我我做错了什么。它告诉我一些关于lisp解析器如何幕后工作的具体信息。(其中一些在clojure.org上有文档记录)。

由于这对我这个新用户没有任何帮助,我将去谷歌搜索。
对于错误,如参数顺序错误或调用不能调用的方法,初学者对搜索错误信息的容忍度并不高。

正是在这里,你给用户的印象是,错误提示对用户来说并不重要。
(顺便说一句,你处理(nil 2 3)相当好,没有抛出NPE)

这就是Clojure(实现)积极地参与“巨大的学习曲线”神话的地方。
既然我已经看到这个错误很多次了,我知道它是怎么回事。(关于Sean的评论)

损失已经产生。

其他Clojure方言在这方面做得更好。(例如,CLJS和Sci)。

说到努力:有人认为提供更好的错误消息会影响性能。如果我们在这里谈论异常处理(在上面两种情况下我们确实如此),
我不太明白性能影响在哪里。
由于s-expr失败而产生的异常处理不应该在性能关键路径上,我认为。
很高兴更好地理解性能方面是如何介入的。

此外,仅仅为了上面的两个示例错误消息,以用户的角度而不是系统的角度编写它们并不困难。其中一个意味着更改RT.java中的第557行

更好的错误消息可能意味着捕获更底层的异常,将它们与当前的AST关联,并重新抛出与生成错误的列表元素相关的消息。

我恳请您引导初学者获得最好的REPL。
让它在入门和了解Clojure时成为默认设置。
然后你可以拉回帘幕说:你知道,下面是IFn和ISeq,你可能看到Java堆栈跟踪。

当走Clojure之路时,有很多新概念要学习和理解。重新解释错误消息不应该是其中之一。

by
这里还有另一个Clojure新手遇到ClassCastException的情况。

https://www.reddit.com/r/Clojure/comments/gzudzn/problem_defining_fibonacci_function/
by
"好吧,依然没有介绍如何启动REPL的信息。" -- 看到你对这个回答的评论后我意识到,安装说明没有提到安装后的操作,所以我向入门页面提交了一个PR,添加了一行启动clj或clojure执行REPL的建议。Alex已经合并了这个建议,希望这能带来一些改进,尽管是小小的改进。
by
感谢,Sean。
对于对这些概念习以为常的新手来说,重读文档并不是一件容易的事情。

沿着相同的思路,也许在页面上收集对文档的反馈会很有用。许多公司在尝试改进面向非专家的文档时会这么做。也许可以添加一个像这样更好的反馈按钮:https://docs-feedback.aws.amazon.com/feedback.jsp?feedback_destination_id=068c0217-1615-4028-b3f7-7de2fb227b77&topic_url=https://aws.amazon.com/getting-started/hands-on/build-web-app-s3-lambda-api-gateway-dynamodb/module-one/
(例如)
...