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> cannot be cast to clojure.lang.IFn... 这样的消息可以改写为更容易理解的初学者友好型语言(如 Expected a function - found a <whatever>)。

类似的动态钩子用于筛选堆栈帧,并将“打印”它们到字符串中,将允许对初学者更为友好的输出。

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

当看到“无法转换”错误时,我希望看到实际无法转换的值,而不是“类Symbol无法转换为ISeq”这样的错误信息,而是看到“类Symbol (`the-actual-symbol`) 无法转换为 ISeq”,这样更容易找出错别字或问题所在。当然,许多不透明的类无法有意义的转换为字符串,但我觉得有总比没有好。我明白这些错误来自Java层面,因此添加更多信息可能不是那么简单,但这仍然是个好主意。

我经常遇到另一种问题,那就是既难以直接读懂又晦涩难解的规范错误,要么用肉眼难以上下其义,要么我必须使用某些库来美化打印(为什么我需要库来使语言错误可读?)。我希望看到“调用defn不符合规范”的易懂原因。可能除了(而不是替代)当前输出外,还提供这种信息。
当你遇到这样的语法或规范问题时,请到这里来并提交一个问题!

ClassCastException异常是由jvm生成的(在每一个函数调用中都是如此),所以我们真的不可能增强这个特殊情况(除非我们在每次转换周围发出更多代码)。但即使对这些,如果你遇到令人困惑的错误,请到这里来并提交一个问题。
在REPL或测试中生成更多的代码将是件好事,老实说。

如果Clojure默认运行模式可以包裹内容,以便清楚地以Clojure语义而不是其实施细节显示错误,我想这对于错误信息来说将是非常好的。

1 个回答

+4

这里至少有两个,也许还有更多不同的问题/想法,分开它们会有帮助。前str/hook的想法似乎是正交的。

严肃的问题——如果错误信息是好的(你说它在这里),并且工具从未显示堆栈跟踪,那么为什么这是一个初学者问题?错误信息试图以给定的宏程度显示源错误的良好位置。

"友好的初学者"这个框架似乎不正确,因为它并没有说明任何关于问题的东西。如果堆栈跟踪可以更清晰地显示,那么这对初学者和非初学者都会有用,我们可以通过具体列举理解堆栈跟踪的挑战来得到帮助。我想到...

  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钩子创建一个专门的“询问”,所以,是的,我应该将其保持分开。

我不知道如何表达这个问题——我看别人也没有很好地提出问题。总是只是“错误信息很差”和“堆栈跟踪令人困惑/令人不知所措”。每年在调查中都排在第1位,这反映了来自……调查……近50%(今年)人们的某种“需求”。我想我可以至少开始一场对话。

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

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

希望初学者——以及教初学者的人们——能够提出具体意见。
我继续认为堆栈跟踪的最大问题仅仅是工具在不应该的时候展示了它们。当然,欢迎更多的具体问题被提问(尽可能保持孤立有助于投票和优先级)。

编辑
是的,如果人们在提供反馈时提供他们的编辑器/REPL设置的详细信息,那将是有帮助的。

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

(Alex已经提供了编辑器使用与优先级改进领域的交叉表,很显然,Emacs/CIDER的堆栈跟踪与该编辑器的增加抱怨不相关——错误信息在整个编辑器中使用比例大约为50%)
...