Як атрымаць лепшую зваротную сувязь ад памылак 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 + %))))

Here, I mistakenly thought that group-by returns a list of lists, but it actually returns a map of > or however you'd phrase it in Java terms. This gives an error message that says:

<�Р> ClassCastException clojure.lang.PersistentVector не можа быць прыведзены да java.lang.Number clojure.lang.Numbers.add (Numbers.java:126)

Гэта не вельмі карысна, таму што гэта не трасіроўкі стэка. Калі я тыпу (е) ён кажа:

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)

Я паняцця не маю, як я магу пайсці ад гэтага паведамленні пра памылку ў разуменні, «Вы думалі, што вы праходзілі ў спіс спісаў у Карта , але вы сапраўды пераходзячая ў карце тыпу дадзеных». Трасіроўка стэка паказвае, што праблема паведамлялася ўнутры </паменшыць код>, а не ўнутры групавы функцыяй , але ИМО, гэта не там, дзе я, як чалавек зрабіў сваю памылку. Вось толькі дзе праграма выявіла памылка была зроблена.

Такія праблемы, як яны могуць прыняць мяне 15+ хвілін, каб вырашыць. Як я магу зрабіць гэта зойме менш часу?


Я ведаю, што гэта занадта шмат, каб чакаць, дынамічны мову, каб злавіць гэтыя памылкі. Але я адчуваю, што паведамленні пра памылкі іншых дынамічных моў, як Javascript значна больш карыснымі.

Я атрымліваю вельмі адчайным тут, таму што я кадаваньне ў Clojure на працягу 1-2 месяцаў, і цяпер я адчуваю, што павінна мець лепшую ручку высветліць гэтыя праблемы. Я паспрабаваў з дапамогай : папярэдне /: пост на функцыі, але што ёсць некаторыя праблемы

  1. Справаздачнасць па : папярэдне /: пост хрэнова. Ён толькі друкуе літаральна тое, што вы выпрабоўваеце. Так што, калі вы не прыклалі шмат намаганняў у яго, паведамленне пра памылку ня карысна.
  2. Гэта не адчувае сябе вельмі ідыёматычны. Адзіны код, які я бачыў, што выкарыстанне : папярэдне /: пост з'яўляюцца артыкулы, якія тлумачаць, як выкарыстоўваць : папярэдне /: пост .
  3. Гэта рэальная боль, каб выцягнуць этапы разьбы макраса ў іх уласны defn S, так што я магу паставіць : папярэдне /: пост ў іх.
  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. Я павінен пісаць нашмат больш модульных тэстаў для майго кода Clojure, чым я магу зрабіць для майго Java кода. Нават калі я пішу код аднаразовыя.
24
@RyanMoore: Так, усё гэта. Я звычайна набіраю свой код у маіх зыходных файлах і выкарыстоўваць Repl паспрабаваць код. Я змяняю крыніца, заснаваны прэч зваротнай сувязі я атрымліваю.
дададзена аўтар Daniel Kaplan, крыніца
@RyanMoore: Так, усё гэта. Я звычайна набіраю свой код у маіх зыходных файлах і выкарыстоўваць Repl паспрабаваць код. Я змяняю крыніца, заснаваны прэч зваротнай сувязі я атрымліваю.
дададзена аўтар Daniel Kaplan, крыніца
@RyanMoore падумаў я і быў упэўнены, што групы па працаваў менавіта так. Гэта было няправільна, але людзі робяць памылкі. Калі б было нешта сказаць мне (напрыклад: кампілятар), што я не выкарыстоўваю групы, па правільна, гэта было б відавочна. Але я толькі атрымаць інфармацыю, што я не выкарыстоўваю + правільна. Выявіўшы, што групы па была крыніца маёй праблемы спатрэбілася шмат часу, і я пытаюся, як атрымаць лепшую зваротную сувязь па гэтым пытанні.
дададзена аўтар Daniel Kaplan, крыніца
@RyanMoore падумаў я і быў упэўнены, што групы па працаваў менавіта так. Гэта было няправільна, але людзі робяць памылкі. Калі б было нешта сказаць мне (напрыклад: кампілятар), што я не выкарыстоўваю групы, па правільна, гэта было б відавочна. Але я толькі атрымаць інфармацыю, што я не выкарыстоўваю + правільна. Выявіўшы, што групы па была крыніца маёй праблемы спатрэбілася шмат часу, і я пытаюся, як атрымаць лепшую зваротную сувязь па гэтым пытанні.
дададзена аўтар Daniel Kaplan, крыніца
@RyanMoore Так, гэта было б карысна
дададзена аўтар Daniel Kaplan, крыніца
@RyanMoore Так, гэта было б карысна
дададзена аўтар Daniel Kaplan, крыніца
Чаму людзі маркіроўка гэта як неканструктыўная? Я атрымліваю тоны канструктыўнай зваротнай сувязі з гэтым, што робіць гэта вельмі стаіць пост.
дададзена аўтар Daniel Kaplan, крыніца
Там у адпаведнае абмеркаванне адбываецца на групах Google Clojure толькі цяпер - абнадзейлівы сюжэтная лінія з'яўляецца (двукоссі з'яўляюцца часткай сюжэтнай лініі).
дададзена аўтар Michał Marczyk, крыніца
Там у адпаведнае абмеркаванне адбываецца на групах Google Clojure толькі цяпер - абнадзейлівы сюжэтная лінія з'яўляецца (двукоссі з'яўляюцца часткай сюжэтнай лініі).
дададзена аўтар Michał Marczyk, крыніца
Нешта падобнае на гэта: docs.racket-lang.org/stepper
дададзена аўтар user1922460, крыніца
Нешта падобнае на гэта: docs.racket-lang.org/stepper
дададзена аўтар user1922460, крыніца
Ах, у мяне ёсць ты. Такім чынам, вы шукаеце нешта, што крокі па ацэнцы?
дададзена аўтар user1922460, крыніца
Ах, у мяне ёсць ты. Такім чынам, вы шукаеце нешта, што крокі па ацэнцы?
дададзена аўтар user1922460, крыніца
Я думаю, я крыху зьбянтэжаны гэтым: "Вы думалі, што вы праходзілі ў спісе спісаў ў карты, але вы сапраўды пераходзячымі ў карце тыпу дадзеных». Ня адказ на гэтае пытанне проста зводзіцца да добра ведаючы бібліятэку Clojure? І кажаце, што вы не ведалі, што exatly (групавая ідэнтычнасць [1 2 3 1]) вяртае ... Ацэньваючы яго ў Рэпля бы паказаць вам? Я магу быць недастатковае разуменне вашага пытання ...
дададзена аўтар user1922460, крыніца
Вы марнуеце любога з вашага часу Dev з дапамогай REPL?
дададзена аўтар user1922460, крыніца
Вы марнуеце любога з вашага часу Dev з дапамогай REPL?
дададзена аўтар user1922460, крыніца

7 адказы

У дадзеным канкрэтным выпадку выяўлення крыніцы праблемы лёгка:

  1. We've got a function to be applied to a known vector of items. We're also expecting a particular result.

  2. Applying the function results in a problem. Let's peek inside the function then; it happens to be a ->> pipeline.

  3. The most straightforward way to diagnose the problem is to leave off some of the final stages of the pipeline to see if the intermediate stages in the transformation are as we expect.

Выкананне 3. асабліва простая ў Рэпля; адзін падыход заключаецца ў Абарону ўваходныя і прамежкавыя вынікі да часовага Варс, другі з'яўляецца выкарыстанне * 1 , * 2 і * 3 . (Калі трубаправод доўгі або вылічэнні займаюць шмат часу, я б рэкамендаваў рабіць часовы Абарону па меншай меры адзін раз кожныя некалькі крокаў, у адваротным выпадку * п s можа дастаткова.)

У іншых выпадках, вы маглі б зрабіць нешта крыху іншае, але ў любым выпадку, ломку працы на кіраваныя кавалкі, каб гуляць з у Рэпля з'яўляецца ключавым фактарам. Вядома, знаёмства з бібліятэкамі паслядоўнасцяў і збору Clojure ў хуткасці працэсу зусім няшмат; але гуляць з імі ў кантэксце невялікіх кавалкаў рэальнай задачы вы працуеце на гэта адзін з лепшых спосабаў даведацца пра іх.

5
дададзена
Добра, што нейкая вялізная інфармацыя. Я хачу гэтыя «адладкі працоўных працэсаў» былі задакументаваныя лепш дзе-небудзь. Вы ведаеце, дзе я магу даведацца больш трукаў, як гэта?
дададзена аўтар Daniel Kaplan, крыніца
Што * 1 ... * п рабіць? Гэта цяжка Google для гэтага.
дададзена аўтар Daniel Kaplan, крыніца
<�Код> * 1 , * 2 і * 3 трымаць значэнне трох апошніх выразаў ацэньвалі ў Рэпля; см (дакумент * 1) і г.д. У якасці заўвагі, SymbolHound добры для пошуку гэтага тыпу.
дададзена аўтар Michał Marczyk, крыніца
Група нітка Clojure Google я звязаны ў каментарах па гэтым пытанні, безумоўна, варта прачытаць; ёсць таксама стары Debugging ў Clojure? пытанне тут на SO. Вядома, існуюць і іншыя дыскусіі ў абодвух месцах, я проста так, каб памятаць гэтыя два з верхняй частцы маёй галавы. Каб даведацца пра тое, як * 1 (і звязаныя з гэтым пытанні, такія, якія Варс з'яўляюцца установлен! здольны на Рэпля), вы можаце прачытаць крыніца clojure.main ( вось 1.5.1 версія ).
дададзена аўтар Michał Marczyk, крыніца

У дадзеным канкрэтным выпадку выяўлення крыніцы праблемы лёгка:

  1. We've got a function to be applied to a known vector of items. We're also expecting a particular result.

  2. Applying the function results in a problem. Let's peek inside the function then; it happens to be a ->> pipeline.

  3. The most straightforward way to diagnose the problem is to leave off some of the final stages of the pipeline to see if the intermediate stages in the transformation are as we expect.

Выкананне 3. асабліва простая ў Рэпля; адзін падыход заключаецца ў Абарону ўваходныя і прамежкавыя вынікі да часовага Варс, другі з'яўляецца выкарыстанне * 1 , * 2 і * 3 . (Калі трубаправод доўгі або вылічэнні займаюць шмат часу, я б рэкамендаваў рабіць часовы Абарону па меншай меры адзін раз кожныя некалькі крокаў, у адваротным выпадку * п s можа дастаткова.)

У іншых выпадках, вы маглі б зрабіць нешта крыху іншае, але ў любым выпадку, ломку працы на кіраваныя кавалкі, каб гуляць з у Рэпля з'яўляецца ключавым фактарам. Вядома, знаёмства з бібліятэкамі паслядоўнасцяў і збору Clojure ў хуткасці працэсу зусім няшмат; але гуляць з імі ў кантэксце невялікіх кавалкаў рэальнай задачы вы працуеце на гэта адзін з лепшых спосабаў даведацца пра іх.

5
дададзена
Добра, што нейкая вялізная інфармацыя. Я хачу гэтыя «адладкі працоўных працэсаў» былі задакументаваныя лепш дзе-небудзь. Вы ведаеце, дзе я магу даведацца больш трукаў, як гэта?
дададзена аўтар Daniel Kaplan, крыніца
Што * 1 ... * п рабіць? Гэта цяжка Google для гэтага.
дададзена аўтар Daniel Kaplan, крыніца
Група нітка Clojure Google я звязаны ў каментарах па гэтым пытанні, безумоўна, варта прачытаць; ёсць таксама стары Debugging ў Clojure? пытанне тут на SO. Вядома, існуюць і іншыя дыскусіі ў абодвух месцах, я проста так, каб памятаць гэтыя два з верхняй частцы маёй галавы. Каб даведацца пра тое, як * 1 (і звязаныя з гэтым пытанні, такія, якія Варс з'яўляюцца установлен! здольны на Рэпля), вы можаце прачытаць крыніца clojure.main ( вось 1.5.1 версія ).
дададзена аўтар Michał Marczyk, крыніца
<�Код> * 1 , * 2 і * 3 трымаць значэнне трох апошніх выразаў ацэньвалі ў Рэпля; см (дакумент * 1) і г.д. У якасці заўвагі, SymbolHound добры для пошуку гэтага тыпу.
дададзена аўтар Michał Marczyk, крыніца

Лепшы спосаб мець сэнс выключэнняў Clojure як цяпер (да таго часу, пакуль мы, верагодна, маем Clojure ў Clojure), каб зразумець, што Clojure рэалізуецца з дапамогай класаў Java, інтэрфейсы і г.д. Такім чынам, кожны раз, калі вы атрымліваеце любое такое выключэнне паспрабаваць супаставіць класы/інтэрфейсы згадваецца у выключэнне паняццяў Clojure.

Для былых: У бягучым выключэннем яго можна лёгка зрабіць выснову, што clojure.lang.PersistentVector судзіўся ўвесці адценне java.lang.Number ў метадзе clojure.lang.Numbers.add . З гэтай інфармацыяй вы можаце паглядзець у код і інтуітыўна высветліць, дзе вы карыстаецеся дадаць т + у кодзе, а затым дыягнаставаць гэтую праблему той факт, што як-то гэта + атрымліваюць вектар у якасці параметру замест колькасці.

3
дададзена

Я знаходжу clojure.tools.logging/шпіён макрас вельмі карысны для адладкі. Яна выводзіць абгарнуць выраз, а таксама яго кошт. Калі налада clojure.tools.logging гэта не тое, што вы хочаце зрабіць прама цяпер (гэта патрабуе нармальнай канфігурацыі пратакалявання Java), вы можаце выкарыстоўваць гэта:

(defmacro spy
  [& body]
  `(let [x# [email protected]]
     (printf "=> %s = %s\n" (first '~body) x#)
     x#))

* Мець на ўвазе, што прыведзены вышэй код ня друкуе з значэнняў лянівым SEQ калі яна не была рэалізаваная. Вы можаце VEC лянівым след, каб прымусіць яго рэалізацыі - не рэкамендуецца для бясконцых seqs.

На жаль, я не знайшоў добры спосаб, каб выкарыстоўваць шпіён макрас ў заправачных макрас, але яно павінна быць дастаткова для большасці іншых выпадкаў.

2
дададзена
Чаму гэта не працуе з заправачным макраса?
дададзена аўтар Daniel Kaplan, крыніца
<�Код> (- >> хз шпіён (фільтр даже)) будзе працаваць нармальна.
дададзена аўтар qerub, крыніца
- >> резьб макрас проста ўстаўляе ўваход у якасці апошняга элемента ў форме. (- >> хз (шпіён (фільтр даже)) ператвараецца ў (шпіёнскай (фільтр даже) хз), які, відавочна, няправільна ?.
дададзена аўтар David L, крыніца

Я знаходжу clojure.tools.logging/шпіён макрас вельмі карысны для адладкі. Яна выводзіць абгарнуць выраз, а таксама яго кошт. Калі налада clojure.tools.logging гэта не тое, што вы хочаце зрабіць прама цяпер (гэта патрабуе нармальнай канфігурацыі пратакалявання Java), вы можаце выкарыстоўваць гэта:

(defmacro spy
  [& body]
  `(let [x# [email protected]]
     (printf "=> %s = %s\n" (first '~body) x#)
     x#))

* Мець на ўвазе, што прыведзены вышэй код ня друкуе з значэнняў лянівым SEQ калі яна не была рэалізаваная. Вы можаце VEC лянівым след, каб прымусіць яго рэалізацыі - не рэкамендуецца для бясконцых seqs.

На жаль, я не знайшоў добры спосаб, каб выкарыстоўваць шпіён макрас ў заправачных макрас, але яно павінна быць дастаткова для большасці іншых выпадкаў.

2
дададзена
Чаму гэта не працуе з заправачным макраса?
дададзена аўтар Daniel Kaplan, крыніца
<�Код> (- >> хз шпіён (фільтр даже)) будзе працаваць нармальна.
дададзена аўтар qerub, крыніца
- >> резьб макрас проста ўстаўляе ўваход у якасці апошняга элемента ў форме. (- >> хз (шпіён (фільтр даже)) ператвараецца ў (шпіёнскай (фільтр даже) хз), які, відавочна, няправільна ?.
дададзена аўтар David L, крыніца

Dynalint might be worth looking into. It wraps function calls with extra checks that hurt performance, but provide better error messages.

Гэта, здаецца, не быць вельмі спелым праектам, і не было абноўлена на працягу года, але яна ўжо мае некаторы прагрэс, каб палепшыць паведамленні пра памылкі. Акрамя таго, ён знаходзіцца ў спісе на магчымы GSoC 2015 праектаў, такім чынам, мы маглі б убачыць вялікае паляпшэнне ў бліжэйшы час!

1
дададзена
Dynalint мае купаваны развіццё: /
дададзена аўтар user1492534, крыніца

Dynalint might be worth looking into. It wraps function calls with extra checks that hurt performance, but provide better error messages.

Гэта, здаецца, не быць вельмі спелым праектам, і не было абноўлена на працягу года, але яна ўжо мае некаторы прагрэс, каб палепшыць паведамленні пра памылкі. Акрамя таго, ён знаходзіцца ў спісе на магчымы GSoC 2015 праектаў, такім чынам, мы маглі б убачыць вялікае паляпшэнне ў бліжэйшы час!

1
дададзена
Dynalint мае купаваны развіццё: /
дададзена аўтар user1492534, крыніца