24

コードに含まれる 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 のような他の動的言語のエラー メッセージの方がはるかに役立つと思います。

私は 1 ~ 2 か月前から clojure でコーディングしており、これらの問題をより適切に解決できるようにする必要があると感じているため、ここでかなり必死になっています。関数で:pre/を使用してみましたが、いくつか問題があります:post

  1. :pre/のレポート:postはひどいものです。テストしたものを文字通り印刷するだけです。そのため、多くの労力を費やさない限り、エラー メッセージは役に立ちません。
  2. これはあまり慣用的な感じではありません。:pre/を使用する唯一のコードは、/:postの使用方法を説明する記事です。:pre:post
  3. /を挿入できるように、スレッド化マクロのステップを独自defnの sに引き出すのは本当に面倒です。: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 の汚い小さな秘密です / 他の誰もが動的言語に慣れており、エラーを解決するために私が行っているのと同じ努力をすることを期待しています
  4. CounterClockWise にはバグがあり、私の生活を必要以上に難しくしています
  5. Java コードよりも Clojure コードのユニット・テストの方がはるかに多くのユニット・テストを作成することになっています。使い捨てのコードを書いていても。
4

4 に答える 4

3

現時点で clojure の例外を理解する最善の方法 (おそらく clojure に clojure があるまで) は、clojure が Java クラスやインターフェースなどを使用して実装されていることを理解することです。 Clojure の概念を除いて。

例:現在の例外では、 methodclojure.lang.PersistentVectorに型キャストしようとしていたことが簡単に推測できます。この情報から、コードを調べて、コード内で使用している場所を直感的に把握し、この + が数値ではなくパラメーターとしてベクトルを取得しているという事実によって、その問題を診断できます。java.lang.Numberclojure.lang.Numbers.addadd+

于 2013-06-04T04:30:01.730 に答える
1

Dynalintは検討する価値があるかもしれません。パフォーマンスを低下させる余分なチェックで関数呼び出しをラップしますが、より良いエラー メッセージを提供します。

これはあまり成熟したプロジェクトではないようで、1 年間更新されていませんが、エラー メッセージを改善するためにすでにある程度の進歩を遂げています。また、可能なGSoC 2015 プロジェクトのリストに載っているため、すぐに大きな改善が見られる可能性があります!

于 2015-02-23T09:18:12.807 に答える