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

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

0
Spec
当某个函数的仪器仪表失败时,explain-data会包含“调用者”信息。然而,如果仪器仪表失败是针对宏的,则此信息将缺失。

这个评论使我相信,预期行为是使explain-data包含此信息,以便第三方错误打印机可以显示它。

在下面的重现中,我设置了自定义打印机来仅捕获原始explain-data(这不是一个有用的打印机,只是用来显示正在发生的事情)

重现


  (require '[clojure.spec.alpha :as s])
  (require '[clojure.spec.test.alpha :as st])
  (require '[clojure.specs.alpha :as s])


  (s/fdef my-fn
          :args (s/cat :x int?))
  (defn my-fn [x]
    x)

  (s/fdef my-macro
          :args (s/cat :x int?))
  (defmacro my-macro [x]
    x)

  (st/instrument)
  (def !ed (atom nil))
  (set! s/*explain-out* (fn [ed]
                          (reset! !ed ed)))
  (my-fn "")
  @!ed
  ;; {:clojure.spec.alpha/problems [{:path [:args :x], :pred clojure.core/int?, :val "", :via [], :in [0]}], :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__2436 0x72029b0e "clojure.spec.alpha$regex_spec_impl$reify__2436@72029b0e"], :clojure.spec.alpha/value (""), :clojure.spec.alpha/args (""), :clojure.spec.alpha/failure :instrument, :clojure.spec.test.alpha/caller {:file "form-init8333540581183382896.clj", :line 548, :var-scope expound.alpha/eval27394}}

  ;; ^--- 注意这里有一个 :clojure.spec.test.alpha/caller条目

  (my-macro "")
  @!ed

  ;; #:clojure.spec.alpha{:problems [{:path [:args :x], :pred clojure.core/int?, :val "", :via [], :in [0]}], :spec #object[clojure.spec.alpha$regex_spec_impl$reify__2436 0x479a6a73 "clojure.spec.alpha$regex_spec_impl$reify__2436@479a6a73"], :value (""), :args ("")}

  ;; ^--- 没有调用者信息

10 个答案

0

评论者:alexmiller

无法向宏添加编程信息,因此该票据的部分内容在书面表达上没有意义。但我想你指的是宏展开期间的规范检查。

在宏检查的情况下,调用者信息是编译器所知的,包括在包装 CompilerException 中。我想这些信息可以从编译器传递给 s/macroexpand-check,并可能产生与已编程的函数调用相似的结果。

0

评论者:bbrinck

哦,你是对的,谢谢你的澄清。

如果增加了调用者信息,对于在宏展开期间失败的规范,也很有必要增加一个特定的 :clojure.spec.alpha/failure 值。这样第三方工具就可以在宏展开失败时显示特定类型的错误消息。

0

评论者:bbrinck

此外,访问宏名称的符号将有助于错误报告。

0

评论者:sohta

在我看来,关于编程失败和宏规范错误的 {{explain-data}} 应该包含函数(或宏)的 fspec,就像我在 CLJ-2218 中之前建议的那样。

一个 {{fspec}} 在其元数据中包含有关规范函数的有用信息(例如.ns-qualified 名称符号和行号等),因此规格错误报告者可以它们来为编程失败和宏规范错误提供更丰富、更精确的错误消息,并以统一的方式。

0

评论者:alexmiller

在 CLJ-2373 之后,规范宏失败的调用者信息将通过包装 CompilerException 传递(如其他编译器和宏展开错误)。应考虑是否这个问题本质上得到了解决。

0

评论者:bbrinck

很高兴听到这将被包含在 CompilerException 中,但我认为仍然将其包含在 spec explain-data 中是有用的。

在打印规范错误时,如果能将宏展开规范错误和仪器错误以类似格式打印出来(如果用户选择自定义规范打印器),将更加清晰。在宏展开错误和仪器错误的情况下,在不同位置查找调用信息可能会造成混乱。

此外,规范打印器可能会选择使用调用信息做一些新颖的事情,比如为输出添加颜色或打印实际代码而不是行号。对于规范打印器来说,能够自定义所有这些信息将很棒。

0

评论者:alexmiller

这里的问题是,在宏规范错误创建的点,我们无法得知正确的调用信息。那时的堆栈是编译器宏展开,而非调用代码。宏展开知道这个上下文,这就是它添加CompilerException(CompilerException的主要目的)的原因。

我认为为了改变这一点,我们需要将信息在规范检查之外传递。好吧,可能这行得通。我会再次看看。

0

评论者:bbrinck

在最近的消息错误工作(伟大的工作!)中,符号似乎可用,但尚未传递给"explain-out"函数:[链接](https://github.com/clojure/clojure/blob/b182982007df934394f0bc68b3a238ca9f200dd1/src/clj/clojure/main.clj#L268-L279)

将此"assoc"到传递给"spec/explain-out"的"spec"中是否可行?这将允许自定义打印器引用规范名称,这将非常有用。

另一种方法是让用户在REPL中设置自定义错误处理器,但这比设置"explain-out"的第三方实现要复杂得多。

0
_由 alexmiller 发表的评论:

我不愿意使这个特性只能由clojure主REPL使用,如果使用其他REPL,最好始终这样做,因此最好在构建时进行。我现在对此有更好的把握,但不知道是否已经太晚了,无法用于1.10。
0
参考:https://clojure.atlassian.net/browse/CLJ-2271(由bbrinck报告)
...