2024年Clojure状态调查! 中分享您的看法。

欢迎!请参阅 关于 页面以了解有关如何使用本站的一些更多信息。

+2
错误

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

2 个答案

+1

有多个库可用于修改堆栈跟踪的打印方式,提供诸如展开、Clojure 框架和着色等功能。我们目前没有计划在新版的 Clojure 中进行此类修改。

你能指出哪些是这些库吗?
米歇尔,如果你还没有使用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)。
Expected a function -- found: java.lang.Long
user=>
+1

这里是另一个关于获得更好的赞成错误信息的投票。

下面提到的库或多或少成功地处理了症状,但让我们从一个想要学习Clojure的人的角度来看。

好的,现在我知道如何下载它。我可能试着通过使用 repl.it 去体会一下它。
现在我有了基本的经验(与 clojure.org 的混淆错误消息相比)(见: "你可能会看到的一种令人困惑的错误是意外尝试将数据列表评估为代码: " )

好吧,我们先去学习Clojure。
好的,仍然没有关于如何启动REPL的内容。
第三个链接:一个好的开始REPL的想法。
第四个链接:这里有很多启动REPL的方法。
这里有运行REPL的其他方法。

没有一项提及:如果你是初学者,请添加clj-stacktrace,然后启动REPL。
你看到为什么这不太实际了吗?

我知道你近年来投入了大量的努力来写更好的文档,我不愿意指出这些问题。在回到如此熟悉的事物的同时,以一个想了解这种新语言的开发者的视角来看,是很不容易的。

我相信你有一次或可能两次机会给人留下好的印象,生动的错误信息告诉你哪里出了错,这是其中很重要的一部分。

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

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

这两者都没有告诉我我哪里做错了。它告诉我后台如何工作的 Lisp 解算器的某些具体细节。(其中一些在 clojure.org 上有记录)。

对于我这个新用户来说,这些都没有帮助,我得去谷歌搜索。
对于初学者来说,在错误信息上进行谷歌搜索,如错误的参数顺序或调用无法调用的函数的错误, tolerance 并不是很高。

这就是让你觉得错误消息指导用户并不是很重要。
(顺便说一句,你处理(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。
从一个对现在来说司空见惯的概念不熟悉的初学者的角度来看,重新阅读文档并不是一件 trivial 的事情。

沿袭同样的思路,也许直接在页面上收集关于文档的反馈会很实用。许多公司在尝试改善针对非专家的文档时都会这样做。也许可以在这一部分添加一个反馈按钮,它的版本比这个略有改进: 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/
(例如)
...