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

欢迎!请查看关于页面了解更多有关如何使用本站的信息。

+2 投票
错误

Clojure的堆栈跟踪相当晦涩难懂,有没有库可以显示更好的错误或格式化堆栈跟踪?

2 个回答

+1 投票

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

您能告诉我哪些是这些库吗?
发表于
Michel,如果您还不是Clojure 1.10.1,我建议您升级。在1.10版本中已经做了很多错误信息的改进,并在1.10.1版本中增加了更好的报告机制。

我还会分享Stuart Halloway关于堆栈跟踪的推文链接: 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)

以上所说,我确实对这种可能性过于天真。在这个语言中,我们存在大量的互操作性,这可能会使得这个过程比在像elm这样的封闭系统中困难得多。
Clojure 可以检测这些条件并抛出更详尽的异常,但这会给所有用户带来性能开销。历史上的选择一直是追求速度并忍受“不寻常”的Java异常,因为每个新用户只需学习这些“一次”,所以这是一个针对大多数用户的良好权衡。

话虽如此,如果能内置一个“友好”的REPL选项就更好了,它使用:caught 提供更友好的错误消息。
例如

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})
                    t)))
#'user/easy
user=> (clojure.main/repl :caught easy)
user=> (1 2 3)
执行错误(ExceptionInfo)在 user/easy(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/
(仅为例子)
...