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这样的封闭系统更困难。
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

这是对即使更好的出错信息的一个+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的loader 'bootstrap'中;clojure.lang.IFn位于名为'app'的无名module中)。

两者都没有告诉我我哪里做错了。它告诉我一些关于如何后台工作lisp解释器的一些具体信息。(其中一些在clojure.org上有文档说明)。

由于这些都没有对我这个新用户有所帮助,我将去Google搜索。
对于错误,如错误的参数顺序或调用无法调用的内容,初学者对搜索错误信息的容忍度很低。

这就是你留下的印象,即用户引导错误消息并不重要。
(顺便说一下,你处理(nil 2 3)相当出色,并没有抛出NPE)

这就是clojure(实现)积极地为“巨大的学习曲线”做法做出贡献的地方。
我无法理解这个问题在哪里——尽管看了很多次这个错误,我最终还是学会了是什么——(关于Sean的评论)。

损害已经造成了。

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

说到努力,有一种反对意见认为提供更好的错误消息会影响性能。如果我们在这里谈论异常处理(以及在上面两个例子中我们确实这样做),
我不确定我懂性能影响在哪里。
由于s-expr失败而进行的异常处理,我认为这不在性能关键路径上。
很高兴更好地理解性能方面是如何发挥作用的。

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

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

我恳请你引导初学者到达他们能获得的最优秀的REPL。
让它成为入门和学习Clojure时的默认。
然后你可以拉下帷幕说:你知道,下面是IFn和ISeq,你可能还会看到Java堆栈跟踪。

在追随Clojure的道路时,有很多新概念要学习和理解。重新解释错误消息不应该成为其中之一。

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/
(例如)
...