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 输入,也适用于 pstexplain),从而将初学者难以理解的错误消息(如 class <whatever> 不能转换为 clojure.lang.IFn...)重写为易于理解的术语(期望一个函数 - 找到 <whatever>)。

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

处理宏或语法错误(拼写错误)时,我经常遇到难以解读的错误。

当我看到“无法转换”错误时,我希望看到实际无法转换的值——而不是“类Symbol无法转换为ISeq”,最好是看到“类Symbol(`the-actual-symbol`)无法转换为ISeq”,这样更容易找到误写的位置。当然,许多不透明的类不能有意义地使用toString,但我认为一些比没有好。我明白这些错误来自Java一侧,因此添加更多信息可能并不简单,但仍然很受欢迎。

我经常遇到难以理解的规范错误,这些错误要么费时费力地分解,要么我必须使用某些库来美化打印(为什么我需要库来使语言错误可读?)。 hopely可以有一个简单的人类可读的理由来解释“调用defn不符合规范”,可能除(不代替)当前输出外。
当您遇到此类语法或规范情况时,请到这里提交问题!

ClassCastException是由jvm生成的(在每次函数调用中都是如此),所以我们实际上不可能增加这个特定的案例(除非我们围绕每次转换发出很多代码)。但这对于这些,如果遇到令人困惑的错误,请在这里提交问题。
在REPL或测试中发出更多的代码会使这些问题变得更好。

如果Clojure默认以运行模式包装某些内容,以便可以清楚地以Clojure语义而不是其实施细节显示错误,那么在REPL或测试中,我认为这将很棒。

1 个答案

+4

这里至少有两个,可能还有更多不同的问题/想法,分开它们会很 helpful。前- str/钩子想法似乎是正交的。

严肃的问题 - 如果错误信息是对的(你说它在这里),并且工具从未显示堆栈跟踪,为什么这是个初学者问题?错误信息试图显示源错误的好位置(在宏的范围内)。

"新手友好型"我认为不是一个正确的话框,因为它没有说任何关于问题的内容。看起来如果堆栈跟踪可以更加清晰,那么这对初学者和非初学者都很有用,我们应该具体列出理解堆栈跟踪的挑战。我想...

  1. Clojure函数调用通常涉及每次调用2个或3个帧
  2. 匿名函数具有晦涩的名称
  3. 在某些情况下,实现隐式创建匿名函数
  4. 用户调用宏,但堆栈跟踪处理展开的代码(你写的代码 != 你运行的代码) - 这是错误信息和堆栈跟踪的根本问题
  5. 宏代码经常创建gensym名称,这些名称成为类名称
  6. REPL基础设施在堆栈中
  7. 工具基础设施在堆栈中(打印机,nrepl,中间件)
  8. Clojure核心库和Clojure Java实现混合
  9. 堆栈跟踪除了显示Clojure命名空间或Java类之外,还冗余地显示文件名
  10. 行号与源代码不相关
  11. 惰性影响

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

请注意,显示现实的不同视角总是与隐藏最初查看堆栈跟踪的原因所需的信息的实际信息的风险存在紧张关系。

如果简化堆栈打印机函数有用,则可以独立于核心完成。特别是开发工具正是从这里进行增强的好位置。

我会说实话,我只是个传声筒。

除了“ClassCastException”这个问题似乎对初学者来说“经常会发生”,而且需要更好的解释之外,我认为消息是好的,我最初打算为“ex-str钩子”创建一个单独的“询问” - 所以,是的,我应该将其分开。

我不知道如何措辞这个问题——而且我也没看到其他人提出得很好。总是只是“错误消息很差”和“堆栈跟踪很混乱/令人沮丧”。每年它在调查中都排名第一,这表明了一种某种“需求”……检查调查……今年近50%的受访者。我想至少先开始一场对话。

我将突出的一点(并且同意),因为每年我都能听到这样的回应

“如果简化的堆栈打印机功能有用,那么这可以独立于核心” —— 但对于初学者来说,第一眼看到的是“核心”,而且人们对这里的“核心”进行了投诉。

希望初学者——以及教初学者的人们——能具体给出意见。
我持续认为,堆栈跟踪的最大问题就是工具在不应该显示它们的时候显示了它们。当然,更多关于具体问题的问题都欢迎(尽可能将其保持为隔离的,有助于投票和优先级)。

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

经过所有在1.10核心错误处理上的五年工作之后,Emacs/CIDER仍然显示大的堆栈跟踪,而且仍然是使用最广泛的环境...

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