clojure - 如何从 Clojure 错误中获得更好的反馈?

标签 clojure runtime-error

与我使用过的所有其他编程语言相比,我发现调试代码中的 Clojure 错误非常困难。我的主要编程语言是 Java,而且对 Clojure 还很陌生。我编写 Clojure 的大部分时间都花在试图弄清楚“为什么我会收到此错误?”我想改变这一点。我使用 CounterClockWise 作为我的主要 IDE。我还不知道如何使用 Emacs(还?)。

这是一个例子:

(ns cljsandbox.core)

(def l [1 2 3 1])

(defn foo
  [l]
  (->> l
    (group-by identity)
    ;vals  ;commented out to show my intent
    (map #(reduce + %))))

这里,我误以为group-by返回一个列表的列表,但它实际上返回一个 <key, list<value>> 的映射或者您可以用 Java 术语来表达它。这会给出一条错误消息:

ClassCastException clojure.lang.PersistentVector cannot be cast to java.lang.Number clojure.lang.Numbers.add (Numbers.java:126)

这不是很有帮助,因为没有堆栈跟踪。如果我输入 (e)它说:

java.lang.ClassCastException: clojure.lang.PersistentVector cannot be cast to java.lang.Number
 at clojure.lang.Numbers.add (Numbers.java:126)
    clojure.core$_PLUS_.invoke (core.clj:944)
    clojure.core.protocols/fn (protocols.clj:69)
    clojure.core.protocols$fn__5979$G__5974__5992.invoke (protocols.clj:13)
    clojure.core$reduce.invoke (core.clj:6175)
    cljsandbox.core$foo$fn__1599.invoke (core.clj:10)
    clojure.core$map$fn__4207.invoke (core.clj:2487)
    clojure.lang.LazySeq.sval (LazySeq.java:42)

我不知道如何从这个错误消息理解“您认为您正在将列表列表传递到map,但您实际上正在传递 map 数据类型”。堆栈跟踪显示问题是在 reduce 内报告的。 ,不在 group-by 内,但在我看来,这不是我作为一个人犯错误的地方。这正是程序发现错误的地方。

此类问题可能需要 15 分钟以上的时间才能解决。我怎样才能缩短这个过程的时间?

<小时/>

我知道期望动态语言能够捕获这些错误有点太过分了。但是,我觉得其他动态语言(例如 javascript)的错误消息更有帮助。

我在这里变得非常绝望,因为我已经用 clojure 编码了 1-2 个月,我觉得我应该更好地解决这些问题。我尝试使用 :pre/:post功能上,但有一些问题

  1. 关于:pre的报道/:post有点糟糕。它仅按字面打印出您测试的内容。因此,除非您付出很多努力,否则错误消息没有帮助。
  2. 这感觉不太惯用。我见过的唯一使用 :pre 的代码/:post是解释如何使用 :pre 的文章/:post .
  3. 将线程宏的步骤提取到自己的步骤中确实很痛苦defn这样我就可以输入 :pre/:post在他们之中。
  4. 如果我虔诚地遵循这种做法,我认为我的代码可能会变得像 Java 一样冗长。我会手工重新发明类型系统。

我已经到了在代码中添加这样的安全检查的地步:

(when (= next-url url)
            (throw (IllegalStateException. (str "The next url and the current url are the same " url))))      
(when-not (every? map? posts-list)
            (throw (IllegalStateException. "parsed-html->posts must return a list of {:post post :source-url source-url}")))

这只修复了第一个要点。

我觉得要么

  1. 我的开发流程非常非常错误,而且我自己也不知道
  2. 有一些我不知道但其他人都知道的调试工具/库
  3. 其他人都遇到过这样的问题,这是 Clojure 的肮脏小 secret /其他人都习惯了动态语言,并期望经历与我相同的努力来解决错误
  4. CounterClockWise 有一些错误,让我的生活变得比实际需要的更加困难
  5. 我应该为 Clojure 代码编写比 Java 代码更多的单元测试。即使我正在编写一次性代码。

最佳答案

在这个特定的例子中,发现问题的根源很容易:

  1. 我们有一个函数可以应用于已知的项目向量。我们也期待一个特定的结果。

  2. 应用该函数会导致出现问题。让我们看看函数内部;它恰好是一个 ->> 管道。

  3. 诊断问题的最直接方法是放弃管道的某些最后阶段,看看转换中的中间阶段是否符合我们的预期。

在 REPL 中,第 3 步的操作特别简单;一种方法是将输入和中间结果 def 到临时变量,另一种方法是使用 *1*2* 3.。 (如果管道很长或者计算需要很长时间,我建议至少每几步执行一次临时 def ,否则 *n 可能会足够了。)

在其他情况下,您会做一些稍微不同的事情,但无论如何,将工作分解为可管理的 block 以便在 REPL 上使用是关键。当然,熟悉 Clojure 的序列和集合库会大大加快该过程;但是,在您正在处理的实际任务的小块上下文中使用它们是了解它们的更好方法之一。

关于clojure - 如何从 Clojure 错误中获得更好的反馈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16901836/

相关文章:

mysql - 在 Clojure 中返回 SQL 查询结果?

java - 二十一点Java : Exception Error

javascript - Stripe 支付示例未显示

excel - If Function - 如果什么都没有,什么也不做

vba - VBA 中的构造函数 - 运行时错误 91 "Object variable not set"

swing - 使用跷跷板启用全屏?

clojure - 评估映射中的函数

c# - 该字段永远不会分配给并且始终具有其默认值 null

clojure - 在Clojure中分割字串

sqlite - 在 Clojure 中通过 clojure.java.jdbc 使用外键约束