2024 Clojure发展现状调查中分享您的想法!

欢迎!请查看关于页面以了解更多关于此操作的信息。

+3
错误

另一项Clojure发展现状调查和“错误信息”仍然是改进的首要任务。

在Slack上的讨论导致围绕异常和错误信息的一系列特定新手痛点。我想在此处关注的焦点是提供(pst)的入门友好替代品。

目前,(pst)在去混淆名称、消除“噪声”和缩小堆栈跟踪方面做出了合理的尝试--但它仍有许多值得期待的地方,对于那些努力理解某些异常并难以在(Java样式)堆栈跟踪中导航的新手来说更是如此,尤其是在与其他一些努力提供用户友好的错误信息和堆栈跟踪的语言相比。

Slack中的一个例子是

(defn f [i]
  (fn [j]
    (/ j i)))

(run! #(% 1) (map f (range 3)))

REPL中打印的异常没问题,但(pst)显示了

user=> (pst)
ArithmeticException Divide by zero
        clojure.lang.Numbers.divide (Numbers.java:190)
        user/f/fn--16571 (NO_SOURCE_FILE:3)
        user/eval16576/fn--16577 (NO_SOURCE_FILE:1)
        clojure.core/run!/fn--8906 (core.clj:7849)
...

省略部分指出了clojure.lang.ArrayChunk.reduce并随后多次引用clojure.core.protocols相关内容(默认不会过分深入地显示对clojure.core/run!的原始调用)。

我认为这里有机会实现一个新的clojure.repl/explain函数,它接受与(pst)相同的参数,提供了对失败更详细的解释,并进一步减少(pst)当前显示的堆栈跟踪中的噪声。

理想情况下,这可以通过在核心中进行一些基本的清理来实现,同时使用一些动态挂钩,允许其他工具“安装”额外的扩展和/或清理,从而使社区可以提供库和功能,进一步提高新手体验的这一方面。

例如,到ex-str的动态挂钩将允许社区提供的工具调整显示的异常消息(既适用于原始REPL输入,也适用于(pst)和(explain)),使初学者难以理解的消息(例如class <something> cannot be cast to clojure.lang.IFn...)可以被重写为新手友好的语言(期望函数 - 找到 <something>)。

类似的动态挂钩可以过滤堆栈帧,并将它们“打印”到字符串中,为初学者提供更友好的输出。

在处理宏或语法错误(错别字)时,我经常会遇到难以解密的错误。

当我自己看到“无法转换”错误时,我想要看到“无法转换”的真正值——而不是“class Symbol 不能转换成 ISeq”——最好是“class Symbol (`真正的符号`) 不能转换成 ISeq”,这样更容易确定错别字或问题的位置。当然,很多不可见的类不能有意义的转换成字符串,但我觉得有比没有好。我明白这些错误来自Java的那一侧,所以添加更多信息可能不是轻而易举的事情,但这仍然很令人期待。

我经常发现另一个问题是晦涩且难以阅读的规范错误,这些错误要么需要花费很长时间才能通过肉眼解析,要么我必须使用某个库来美化打印它们(我为什么要使用库来使语言错误可读?)。如果能有一个人类可读的解释说明“对 defn 的调用不符合规范”的原因,那就太好了。可能除了当前输出以外,还要另外提供。
by
当你遇到此类语法或规范案例时,请来这里提交一个问题!

ClassCastException 异常是由jvm(在字面意义上的每个函数调用中)生成的,所以对我们来说,我们真的无法增强这种情况(除非我们在每个转换周围生成大量的代码)。但是对于这些,如果你遇到一个令人困惑的错误,也请来这里提交一个问题。
by
在REPL或测试中,围绕这些错误排放更多的代码将是非常好的。

如果Clojure默认运行模式可以包装东西,以便在REPL或测试时明确以Clojure语义而非实现细节显示错误,那么我认为对于错误信息来说,这将是非常好的。

1 答案

+4

这里至少存在两个,可能更多,不同的问题/想法,将它们分开将很有帮助。前缀 Str / 钩子想法似乎是正交的。

严肃的问题 - 如果错误信息是好的(你说在这里是),并且工具从未显示堆栈跟踪,为什么这是一个初学者的问题?错误信息尽力显示源代码错误的良好位置(在宏的程度上)。

我认为,“易于初学者”并不适合描述这个问题,因为它并没有说关于问题的任何事情。如果堆栈跟踪可以更清楚地显示,那么它对初学者和高手都有用,并且我们可以通过具体列出理解堆栈跟踪的挑战来得到帮助。

  1. Clojure 函数调用通常涉及每个调用 2 或 3 个帧。
  2. 匿名函数具有不易理解的名称。
  3. 在某些情况下,匿名函数由实现隐式创建。
  4. 用户调用宏,但堆栈跟踪处理展开的代码(你写的代码 != 你运行的代码) - 这是错误信息和堆栈跟踪的根本问题。
  5. 宏代码通常创建导致类名的 gensym 名称。
  6. REPL 基础设施在堆栈中。
  7. 工具基础结构在堆栈中(打印器、nrepl、中间件)。
  8. 混合 Clojure 核心库和 Clojure Java 实现。
  9. 堆栈跟踪冗余地显示文件名,同时在 Clojure ns 或 Java 类中。
  10. 行号不与源正确关联。
  11. 惰性效应。

“简化后的堆栈跟踪”函数需要明确表示哪些这些问题或其他潜在问题实际上很重要。其中一些相对简单,而另一些则相当困难。

请注意,显示现实的不同视角总是与隐藏照亮您最初为何查看堆栈跟踪的实际信息的风险相矛盾。

如果简化后的堆栈打印函数有用,那么这可以独立于核心完成。特别是开发工具在这里有很大的改进空间。

坦白说,我只是传话的人。

除了对初学者来说经常出现的 ClassCastException 需要更好的解释外,我认为信息是好的,我最初打算为 ex-str 钩子创建单独的“询问” - 因此,是的,我应该将其保持分离。

我不知道如何措辞问题 - 我也没有看到其他人很好地措辞它。它总是“错误信息很差”和“堆栈跟踪令人困惑/压倒性”。它在调查中每年都是第一名,这表明...检查调查...近 50% 的受访者(今年)。我想我至少开始一个对话。

我要强调的一点(并且表示同意),因为我每年都能听到这样的回应

"如果简化堆栈打印机功能是有用的,那么这可以与核心独立进行" —— 但新手第一次接触的是“核心”,在这里人们抱怨的也是“核心”。

希望新手以及教授新手的人们能具体提出意见和建议。
我仍然认为堆栈跟踪的最大问题仅仅是工具在不应显示时展示了它们。当然,欢迎就具体问题提出更多问题(并尽量将它们隔离,有助于投票和优先级安排)。

编辑
如果人们在提供反馈时能够提供他们的编辑器/REPL设置细节,这将有所帮助。

Emacs/CIDER即使在1.10对错误进行大量核心工作五年之后,仍然显示大的堆栈跟踪,而且它仍然是使用最广泛的编辑器配置...

(Alex提供了一个编辑器使用与优先级改进区域的交叉表,很明显,Emacs/CIDER的堆栈跟踪与该编辑器的投诉增加并没有相关性——错误消息占所有编辑器的投诉的大约50%)
...