2024 Clojure状态调查!中分享你的想法。

欢迎!请参阅关于页面以获取更多关于如何使用此功能的详细信息。

+2
错误

Clojure堆栈跟踪通常较为晦涩难懂,是否有库可以更好地显示错误或格式化堆栈跟踪?

2 答案

+1

有多个库可用于更改堆栈跟踪的打印方式,提供例如折叠、Clojure框架和着色等功能。我们目前还没有计划在下一个Clojure版本中对这个问题进行改进。

你能指给我这些库是哪些吗?
米歇尔,如果你还没有升级到 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)

话虽如此,我确实天真地认为这是可行的,因为这个语言有大量的重用,这可能会使这比在 like 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

这是对从盒中提供的更佳错误信息的一个+1。

下面提到的库在某种程度上成功处理了症状,但让我们从一个想要学习Clojure、因此访问Clojure.org、点击“入门”的人的角度来看。

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

好吧,让我们先去学习Clojure。
好的,关于如何启动REPL,还是什么都没有。
第三个链接:启动REPL是个好主意。
第四个链接:这里有启动REPL的n种方式。
这里有其他运行REPL的方法。

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

我知道你近年来在编写更好的文档上投入了很多精力,我对指出这些事情感到抱歉。对于开发者而言,很难回到如此熟悉的东西,并用想要了解这种新语言的开发者视角去审视它。

我相信你有1到可能2次机会留下好印象,而能够说明用户出错原因的可读性错误信息正是其中重要的一部分。

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

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

这两个都没有告诉我我犯了什么错误。它们告诉我一些关于lisp评估器如何在幕后工作的具体信息。(其中一些在clojure.org上有所记录)。

由于这些对我来说都没有帮助,作为一名新用户,我将去谷歌搜索。
对于初学者而言,谷歌搜索错误消息(如错误参数顺序或调用无法调用的内容的错误)的容忍度不高。

这就是你认为错误信息引导用户不是重要的印象所在。
(顺便提一下,你处理(nil 2 3)得很好,并没有抛出NPE)

这就是clojure实现(clojure)积极贡献到‘巨大学习曲线’这一说法的地方。
即使我经常看到这个错误,我也不认为我已经学会了它。(关于Sean的评论)。

伤害已经造成。

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

说到努力:有一种反对意见认为提供更好的错误信息会影响性能。如果我们在这里谈论异常处理(在上面两个例子中都是这样),
我不确定我理解性能影响的所在。
对于s-expr失败的异常处理不应该在性能关键路径上。
我很愿意更深入地了解性能方面是如何发挥作用的。

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

更好的错误信息可能意味着捕获更底层的异常,将它们与当前的AST关联,并重新抛出,附带与导致错误的消息关联的列表元素。

我恳请你们引导初学者获得最好的REPL。
在入门教程和学习Clojure中将其设为默认。
然后你可以说:你知道,下面是IFn和ISeq,你可能会看到Java堆栈跟踪。

在Clojure的道路上,有许多新的概念需要学习和理解。重新解释错误信息不应该是最主要的一个。

by
Again, a clj first-timer stumbling over 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/
(例如)
...