2024年Clojure状况调查中分享您的想法!

欢迎!请参阅关于页面以获取有关此工作方式的一些更多信息。

+2
错误

Clojure的堆栈跟踪很晦涩,难以跟随,有没有用于显示更好的错误或格式堆栈跟踪的库?

2 个答案

+1

有一些库可用于更改堆栈跟踪的打印,提供如折叠、 Clojure 框架和彩色等功能。我们目前没有计划在下一个 Clojure 版本中做这方面的工作。

你能指给我看这些库是哪些吗?
by
Michel,如果您还没有在Clojure 1.10.1上,我建议升级。1.10版本中对错误消息做了大量工作,并在1.10.1中增加了更好的报告机制。

我还会分享Stuart Halloway在Twitter上关于堆栈跟踪的帖子:https://twitter.com/stuarthalloway/status/1148295437448876032

当您刚开始学习Clojure时,堆栈跟踪可能会令人生畏,但学会阅读它们是值得的努力。坦白说,折叠/省略它们并不像您想象的那么有帮助(因为有时重要信息被这些库隐藏起来)。
by
当我听说更好的错误消息时,我本以为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)

尽管如此,我确实很天真地认为这是可能的。我们语言中有大量的交互操作,这使得这比在封闭系统elm中要困难得多。
by
Clojure可以检测这些条件并抛出更详细的异常,但这会给每个人带来性能开销。历史上的选择始终是牺牲性能以换取"不常见的"Java异常,因为每个新来者只需“一次性”学习这些异常 —— 因此,这对绝大多数用户来说是一个好的折中方案。

话虽如此,如果有一个内置的“友好”REPL选项,使用:caught提供更友好的错误消息,那将非常不错。
by
举例来说:

user=> (defn easy [t]
                (clojure.main/repl-caught
                    (if (re-find #"cannot be cast to clojure.lang.IFn" (ex-message t))
                                                                                                                                                                                                                  
                    (ex-info (str "Expected a function -- found: " (second (re-find #"^([^ ]*)" (ex-message t)))) {:cause t})
#'user/easy
user=> (clojure.main/repl :caught easy)
user=> (1 2 3)
执行错误(ExceptionInfo)在 user/easy (REPL:1)。
预期的是一个函数 —— 找到一个java.lang.Long
user=>
+1
by

对更好的错误消息表达支持。

下述提到的库基本上成功地处理了症状,但让我们从一个想学习Clojure的人的角度来看,他们前往Clojure.org,点击"入门"。

现在我知道如何下载它。也许我会尝试使用《repl.it》来感受它。
现在我有了最基本的体验(带有 clojure.org 混淆的错误消息)(见:"你可能看到的令人困惑的错误可能是由意外尝试将数据列表评估为代码而产生的:")

好,让我们首先去学习Clojure。
好,仍然没有什么关于如何启动REPL的信息。
第三个链接:启动REPL是个好主意。
第四个链接:这里有n种启动REPL的方法。
这里有其他运行REPL( Reads-Evaluates-Prints Loop,读取-计算-打印循环)的方法。

没有任何一种提到:如果你是初学者,让我们添加clj-stacktrace,然后启动REPL。
你明白这为什么不切实际吗?

我知道你近年来投入了很多精力来改进文档,我对此指出这些事项感到很抱歉。很难回到如此熟悉的事物,并以想要了解这种新语言的开发者的视角来看待它。

我相信你只有1到2次机会留下良好的印象,而可读的错误信息,可以告诉你哪里出错,这是其中很重要的一部分。

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

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

这两个都没有告诉我我错在哪里。它只是告诉我后台如何工作的某些具体信息。(其中一些在clojure.org上有文档说明)。

由于所有这些对我来说都没有帮助,所以作为新用户,我将在谷歌中搜索。
对于初学者来说,像错误的参数顺序或调用无法调用的操作这样的错误,通过谷歌搜索错误消息的忍受度并不高。

这就是你给用户的印象,似乎错误信息引导用户并不重要。
(顺便说一句,奇怪的是,你已经很巧妙地处理了(nil 2 3),而没有抛出NPE)。

这就是Clojure(该实现)积极参与“巨大的学习曲线”这一神话的地方。
我在多次看到这个错误之后才了解到它是什么。(关于Sean的评论)。

损害已经造成。

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

说到努力:有一种反对意见认为提供更好的错误信息会影响性能。如果我们在这里谈论异常处理(并且在上面的两个案例中我们都在这样做),
我不是很明白性能影响在哪里。
由于s-expr失败而进行的异常处理不应该在性能关键路径上,我认为。
乐于更好地了解性能方面的作用。

仅对上面的两个示例错误消息而言,从用户的视角而不是系统的视角编写它们的精力并不大。对于其中之一,意味着更改RT.java中的第557行

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

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

在沿着Clojure的道路前进时,有许多新的概念需要学习和理解。重新解释错误信息不应该是其中之一。

by
这里还有另一个clj初学者在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/
(例如)
...