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

Dan*_*lan 24 runtime-error clojure

我发现在我的代码中调试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 + %))))
Run Code Online (Sandbox Code Playgroud)

在这里,我错误地认为group-by返回列表列表,但它实际上返回了一个地图,<key, list<value>>或者你用Java术语表示它.这会显示一条错误消息:

ClassCastException clojure.lang.PersistentVector无法强制转换为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)
Run Code Online (Sandbox Code Playgroud)

我不知道如何从这个错误消息中理解,"你以为你是在传递一个列表列表map但是你真的传递了一个map数据类型".堆栈跟踪显示问题是在内部报告的reduce,而不是group-byIMO 内部的报告,这不是我作为人类犯下的错误.这就是程序发现错误的地方.

这些问题可能需要15分钟才能解决.我怎样才能减少时间?


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

我在这里非常绝望,因为我现在已经在clojure编写1-2个月了,我觉得我应该更好地解决这些问题.我尝试使用:pre/ :poston函数但是有一些问题

  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}")))
Run Code Online (Sandbox Code Playgroud)

其中只修复了第一个要点.

我也想要

  1. 我有一个非常非常错误的开发过程,我不知道
  2. 那里有一些调试工具/库,我不知道其他人都在做什么
  3. 其他人都有这样的问题,这是Clojure肮脏的小秘密/其他所有人都习惯使用动态语言,并期望我经历同样的努力来解决错误
  4. CounterClockWise有一些错误,使我的生活变得比它需要的更难
  5. 我应该为我的Clojure代码编写比我的Java代码更多的单元测试.即使我正在写一次性代码.

Mic*_*zyk 5

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

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

  2. 应用该功能会导致问题.让我们看看函数内部; 它碰巧是一个->>管道.

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

在REPL中做第3步特别简单; 一种方法是def将输入和中间结果转换为临时Vars,另一种方法是使用*1,*2*3.(如果管道很长或者计算需要花费很多时间,我建议def每隔几步至少执行一次,否则*ns可能就足够了.)

在其他情况下,你会做一些稍微不同的事情,但无论如何将工作分解为可管理的块以便在REPL中播放是关键.当然,熟悉Clojure的序列和集合库会加快这个过程的速度; 但是在你正在进行的实际任务的小块环境中与它们一起玩是了解它们的更好方法之一.

  • `*1`,`*2`和`*3`保存REPL中评估的最后三个表达式的值; 请参阅`(doc*1)`等.作为附注,[SymbolHound](http://symbolhound.com/)适用于此类型的搜索. (6认同)
  • `*1` ...`*n`做什么?这很难谷歌. (2认同)