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)

话虽如此,我确实对实现这种可能性很天真,因为我们的语言中有强大的互操作性,这可能会使这比在像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)在用户/easy(REPL:1)。
预期一个函数--找到:java.lang.Long
user=>
+1
by

这又是为“开箱即用”的更好错误消息投了一个支持票。

以下提到的库更多或更少地成功处理了症状,但让我们从想要学习Clojure的人的角度来看,因此他们访问Clojure.org,点击“入门”。

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

嗯,让我们先学习Clojure。
好了,关于如何启动REPL,仍然什么都没有。
第三个链接:启动REPL是一个好主意。
第四个链接:这里有n种启动REPL的方法。
这里有其他运行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
还有另一位 clojure 初学者遇到了 ClassCastException

https://www.reddit.com/r/Clojure/comments/gzudzn/problem_defining_fibonacci_function/
"好吧,还是没有任何关于如何启动REPL的信息。" -- 在看到你对这个答案的最新评论后,我意识到安装说明中没有提到安装后的操作,所以我向入门页面提交了一个PR,建议在安装后运行clj或clojure以启动REPL。Alex已将其合并,所以希望这是一个改进,即使是很小的改进。
谢谢,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/
(例如)
...