与我使用的所有其他编程语言相比,我发现调试代码中的 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 无法转换为 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
但你实际上是在传递一个地图数据类型”。堆栈跟踪显示问题是在内部报告的reduce
,而不是在内部报告group-by
,而是在 IMO,这不是我作为人类犯错误的地方。这正是程序发现错误的地方。
像这样的问题可能需要我 15 分钟以上的时间来解决。我怎样才能让这花费更少的时间?
我知道期望动态语言来捕捉这些错误太过分了。但是,我觉得其他动态语言(如 javascript)的错误消息更有帮助。
我在这里变得非常绝望,因为我已经在 clojure 中编码了 1-2 个月,我觉得我应该更好地处理这些问题。我尝试在函数上使用:pre
/:post
但这有一些问题
:pre
关于/种类的报道:post
很糟糕。它只会从字面上打印出您测试的内容。因此,除非您付出很多努力,否则错误消息将无济于事。- 这感觉不是很地道。我见过的唯一使用 / 的代码
:pre
是:post
解释如何使用 / 的:pre
文章:post
。 - 将线程宏的步骤拉到他们自己
defn
的 s 中,以便我可以将:pre
/:post
放入其中,这真的很痛苦。 - 如果我虔诚地遵循这种做法,我认为我的代码可能会变得像 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}")))
这只修复了第一个要点。
我觉得要么
- 我有一个非常非常错误的开发过程,我不知道
- 那里有一些调试工具/库,我不知道其他人都这样做
- 其他人都遇到这样的问题,这是 Clojure 的肮脏小秘密/其他人都习惯于动态语言,并希望通过与我一样的努力来解决错误
- CounterClockWise 有一些错误让我的生活变得比需要的更艰难
- 我应该为我的 Clojure 代码编写比为我的 Java 代码编写更多的单元测试。即使我正在编写一次性代码。