Мне очень сложно отлаживать ошибки 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
, чтобы я мог поместить в них: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. Даже если я пишу код.