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

欢迎!请参阅关于页面以了解如何使用本网站的更多信息。

+3
Errors by

另一项 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...可以重写为更友好的语言(期望函数 - 找到 <whatever>)。

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

在网络工作区与宏或语法错误(拼写错误)打交道时,我经常遇到很难解码的错误。

当看到“无法转换”的错误时,我希望能看到“无法转换”的实际值——而不仅仅是“不能将Symbol类转换为ISeq”,而是“不能将Symbol类(`the-actual-symbol`)转换为ISeq”,这样可以更容易地定位typo或问题。当然,很多不透明的类无法有效地转换为toString,但一些总比没有好。我明白这些错误来自Java方面,所以添加更多信息可能不是一件简单的事,但这仍然是一件好事。

我还经常发现一些古怪且难以阅读的规范错误,这些错误要么需要花很长时间才能通过肉眼解析,要么我必须使用一些库来帮我格式化它们(为什么我需要库来使语言错误可读呢?)。最好有一个人可以阅读的原因,例如“调用未遵守规范”,这可能是除了当前输出之外的。
当您遇到这样的语法或规范情况时,请到这里来提交一个问题!

ClassCastException异常由jvm(在实际上每个函数调用中)生成,所以我们实际上无法增强特定的病例(除非在我们每个cast周围发出更多的代码)。但对于这些,如果您遇到一个令人困惑的错误,请到这里来提交一个问题。
在REPL或测试中产生那些代码会更加有用,老实说。

如果Clojure默认设置为运行模式,将事物包装起来以清楚地显示错误,而不是实现细节,我相信对于REPL或测试中的错误消息会很好。

1 个回答

+4
by

这里至少有两个可能更多的问题/想法,将它们分开会很有帮助。ex-str/hook想法似乎是正交的。

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

"面向初学者友好"的表述,我认为并不完全恰当,因为它并没有说任何关于问题本身的内容。如果堆栈跟踪能更有启发性,那么它将对初学者和非初学者都有用,并且我们可以通过具体列出理解堆栈跟踪的挑战来得到帮助。随便想想...

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

一个“简化堆栈跟踪”函数需要清楚地说明哪些这些或其他潜在问题实际上是重要的。其中一些相当直接,而另一些则相当困难。

请注意,展示现实的另一种视角总会与隐藏实际的、能阐明为什么你要查看堆栈跟踪的初始信息的风险形成紧张关系。

如果简化堆栈跟踪打印函数是有用的,那么这可以独立于核心完成。特别是开发工具在这方面处于有利地位。

by
坦白说,我只是传话筒。

除了对于初学者来说经常出现的ClassNotFoundException需要一个更好的解释外,我原以为这些消息是OK的,我曾打算为ex-str hook创建一个单独的“询问” - 所以,是的,我应该把它分开。

我不知道如何措辞这个问题——而且我看不出其他人表达得很到位。总是只有“错误信息不佳”和“堆栈跟踪混乱/压倒性”的说法。每年它都位居调查#1,这反映出某种“需求”——查看调查——几乎50%(今年)的受访者。我想至少开始一场对话。

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

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

希望新手——以及教授新手的众人——能提出具体意见。
by
我依然认为堆栈跟踪的最大问题就是工具在不应显示它们的时候显示了。当然,欢迎更多关于具体问题发问的提问(尽可能地将它们孤立出来有助于投票和优先级)。
by
编辑过 by
是的,如果大家在提供反馈时能提供编辑器/REPL设置的具体细节将大有帮助。

Emacs/CIDER在1.10核心对错误进行所有相关工作后五年内,依然显示大的堆栈跟踪,并且依然是使用最广泛的编辑配置...

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