请在2024 Clojure状态调查中分享您的想法!

欢迎!有关此页面如何运作的更多信息,请参阅关于页面。

+2
错误

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

2 个回答

+1

有一些库可以改变堆栈跟踪的打印方式,提供诸如折叠、Clojure框架和彩色化等功能。我们目前在Clojure下一个版本中尚未计划进行此项工作。

你能指向这些库中的哪一个吗?
by
米歇尔,如果你还没升级到 Clojure 1.10.1,我建议你升级。在 1.10 版本中做了很多错误信息的工作,在 1.10.1 版本中添加了更好的报告机制。

我还会分享有关 Stuart Halloway 的推特关于堆栈跟踪的线程: 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。
你看到为什么这不太实际了吗?

我知道你在过去几年里为完善文档付出了很多努力,我指出这些问题很抱歉。回到如此熟悉的事物,并用一个想要学习这种新语言的开发者的眼光来看,是很困难的。

我相信你有一次或两次机会留下良好的第一印象和容易阅读的错误消息,而这些错误消息告诉你哪里出错,是其中重要的一部分。

用户=> (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中;clojure.lang.IFn位于未命名的模块中)

这两个都没有告诉我我哪里做错了。它告诉我一些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/
(例如)
...