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 < whatever> cannot be cast to clojure.lang.IFn...)改写成入门友好型的语言(Expected a function - found a < whatever>)。

类似的动态钩子用于过滤栈帧,以及将它们打印为字符串,将使输出对初学者更加友好。

我在处理宏或语法错误时,经常会遇到难以识破的错误。

当我看到“无法转换”错误时,我很想看到“无法转换”的确切值--而不是“class Symbol无法转换到ISeq”,最好是看到“class Symbol (`the-actual-symbol`) 无法转换到 ISeq”,这样更容易找到错误/问题的位置。当然,许多不透明的类无法有意义地转换为字符串,但仍觉得有总是比没有好。我明白这些错误来自Java的方面,所以添加额外信息可能并不简单,但这仍然很受欢迎。

我遇到的另一个问题是难以阅读的规范错误,这些错误要么需要很长时间才能肉眼解析,要么我得使用某些库来为我美印它们(我为什么要使用库来使我语言错误可读?)。如果能有一个关于“调用defn不符合规范”的人-readable的原因那就更好了。可能是在现有输出之外,而非替代。
当你遇到这样的语法或规范案例时,请来这里并提交一个问题!

ClassCast异常是由jvm生成的(在 literally每个函数调用中),所以我们实际上真的不能增强那个特定案例(除非我们在每个转换周围发出更多代码)。但即使对这些,如果你遇到让人困惑的错误,请来这里提交一个问题。
说实话,在REPL或测试中发出更多代码会很好。

如果Clojure将默认运行模式设置为将事物封装起来,以便它能够清楚地以Clojure语义而不是其实施细节显示错误,当在REPL或测试中时,我认为这对于错误消息来说会很好。

1 答案

+4

这里至少有两个,也许还有更多,不同的问题/想法,最好将它们分开。前字符串/钩子想法似乎是正交的。

一个严重的问题——如果错误信息是好的(你说在这里),而工具从未显示堆栈跟踪,那么为什么这是初学者问题?错误信息试图显示源错误的良好位置(在可给宏的范围内)。

我认为“适用于初学者”的框架不正确,因为它没有说明任何关于问题的信息。如果堆栈跟踪能更有启发性,那么它将对初学者和非初学者都有用,我们可以通过具体列出理解堆栈跟踪的挑战来帮助我们。头脑一热...

  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%的受访者(今年)的“需求”。我想至少发起一次对话。

我将会强调(并且同意)的一个观点,因为每年我都会听到这样的回应

"如果简化的堆栈打印函数有用,那么这可以独立于核心完成"——但是初学者首先接触的是“核心”,这里人们也在抱怨“核心”。

希望初学者——以及教初学者的人——能具体地参与进来。
... by
我继续认为堆栈跟踪的最大问题仅仅是工具在它们不应该显示的时候显示了它们。当然,欢迎更多地提出关于特定问题的疑问(尽可能将它们隔离,有助于投票和优先级)。
... by
modified by
当提供反馈时,如果人们能提供他们的编辑器/REPL设置的详细信息,这将会有所帮助。

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

(Alex提供了一个交叉表格,显示了编辑器使用和优先改进领域之间的关系,很明显,Emacs/CIDER的堆栈跟踪并未与该编辑器的增加投诉相关——错误信息约占所有编辑器的投诉的一半)
...