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})
                    t)))
#'user/easy
user=> (clojure.main/repl :caught easy)
user=> (1 2 3)
执行错误(ExceptionInfo)在 user/easy (REPL:1)。
预期一个函数 —— 发现:java.lang.Long
user=>
+1
by

这是对更好错误消息的另一个+1。

下面提到的库或多或少成功处理了症状,但让我们从一个想要学习Clojure的人的角度来考虑,因此他们前往Clojure.org,点击“入门”。

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

那么,我们先从学习 Clojure 开始吧。
Ok,关于如何启动 REPL 的信息仍然没有。
第三个链接:以这种方式启动 REPL 是一个好主意。
第四个链接:这里有启动 REPL 的 n 种方法。
这里有其他运行 REPL 的方法。

没有人提到任何关于:如果你是初学者,让我们添加 clj-stacktrace,然后启动 REPL。
你看出为什么这是不实际的吗?

我知道你近年来付出了很多努力改进文档,我感到很难指出这些事情。很难回到这样熟悉的东西,并用一个想学习这门新语言的开发者的眼光来看待它。

我相信你只有1次或2次机会留下好印象和可读的错误信息,告诉用户哪里错了,这是很重要的一部分。

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
这里还有另一位第一次尝试使用 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/
(示例)
...