2017-06-06 11 views
2

私はClojureがエラーメッセージで教えていることをほとんど理解しています。しかし、エラーがどこで発生したのかを知るのはまだ無知です。ここでClojureでどこでエラーが発生したのかを確認してください

は私が

(defn extract [m] 
    (keys m)) 

(defn multiple [xs] 
    (map #(* 2 %) xs)) 

(defn process [xs] 
    (-> xs 
     (multiple)  ; seq -> seq 
     (extract))) ; map -> seq ... fails 

(process [1 2 3]) 

静的型付け言語は、今私は線X上のマップを期待し、Clojureのがこれを行う関数にシーケンスを渡そうとしましたことを私に言うだろう何を意味するかの例です。方法:

ClassCastException java.lang.Long cannot be cast to java.util.Map$Entry 

しかし、私はまだどこでエラーが起こったのか分かりません。明らかにこのインスタンスのためには、3つの関数しか関与していないので簡単です。それらのすべてを簡単に読むことができますが、プログラムが大きくなるにつれて、非常に早く古いものになります。

上から下にコードを読み取る以外のエラーがどこで発生したかを知る方法はありますか?あなたはスタックトレースを印刷することができREPLセッションを持っている場合は

+0

@ChrisMurphy申し訳ありませんが、間違いでした。それを交換するのを忘れました。私は 'multiple'を意味しました。 – TomTom

+0

ここに期待される出力は?ベクトルを渡しているマップにキーを使用する必要があります。 - >(keys [2 4 6])のように動作しません。ここでキーの機能を確認してください:https://clojuredocs.org/clojure.core/keys。 また、ハッシュマップも生成されません。マップ:https://clojuredocs.org/clojure.core/map –

+0

これは間違いの実証であり、そうです。私はエラーが起こった理由についても説明しました。私の予想される出力は、どこでエラーが発生したかを教えてくれるものです。 – TomTom

答えて

2

を。まだアルファベットになっていますが、ツールリングのサポートはまだまだたくさんありますが(うまくいけば)、計測機能はうまく機能しています。

(ns foo.core 
    (:require 
    ;; For clojure 1.9.0-alpha16 and higher, it is called spec.alpha 
    [clojure.spec.alpha :as s] 
    [clojure.spec.test.alpha :as stest])) 


;; Extract takes a map and returns a seq 
(s/fdef extract 
    :args (s/cat :m map?) 
    :ret seq?) 

(defn extract [m] 
    (keys m)) 


;; multiple takes a coll of numbers and returns a coll of numbers 
(s/fdef multiple 
    :args (s/cat :xs (s/coll-of number?)) 
    :ret (s/coll-of number?)) 

(defn multiple [xs] 
    (map #(* 2 %) xs)) 


(defn process [xs] 
    (-> xs 
     (multiple)  ; seq -> seq 
     (extract))) ; map -> seq ... fails 

;; This needs to come after the definition of the specs, 
;; but before the call to process. 
;; This is something I imagine can be handled automatically 
;; by tooling at some point. 
(stest/instrument) 

;; The println is to force evaluation. 
;; If not it wouldn't run because it's lazy and 
;; not used for anything. 
(println (process [1 2 3])) 

(他の情報の中で)このファイルの印刷を実行:として読み取ることができ

Call to #'foo.core/extract did not conform to spec: In: [0] val: (2 
4 6) fails at: [:args :m] predicate: map? :clojure.spec.alpha/spec 
#object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x2b935f0d 
"[email protected]"] 
:clojure.spec.alpha/value ((2 4 6)) :clojure.spec.alpha/args ((2 4 
6)) :clojure.spec.alpha/failure :instrument 
:clojure.spec.test.alpha/caller {:file "core.clj", :line 29, 
:var-scope foo.core/process} 

(2 4 6)に渡された値が述語map?が失敗したため、exctractの呼び出しに失敗しました。その呼び出しはファイル"core.clj"の29行目で行われました。

人が移動する警告は、関数の引数だけをチェックし、戻り値はチェックしないという警告です。リッチ・ヒッキーのデザイン決定です。 a library for that, thoughがあります。

0

(私の現在のアプローチである):

(clojure.stacktrace/print-stack-trace *e 30) 

スタックトレースを印刷するさまざまな方法のためhttp://puredanger.github.io/tech.puredanger.com/2010/02/17/clojure-stack-trace-repl/を参照してください。あなたはproject.cljにこのような依存関係を持っている必要があります。

[org.clojure/tools.namespace "0.2.11"] 

私は上記の方法を使用してスタックトレースを取得していない、しかしちょうどREPLで*eを入力すると、あなたに関するすべての入手可能な情報を提供します正直言ってそれは非常に有用ではないようだ。

まれに、スタックトレースが役に立たない場合は、私は通常、与えられた単一の引数を返す関数への呼び出しを使ってデバッグしますが、その引数を出力する副作用があります。私はこの関数をprobeと呼んでいます。あなたの場合、それはスレッドマクロの複数の場所に置くことができます。

+0

'* e'は、エラーがベースライブラリでどこで起こったかを教えてくれます。しかし、私は自分のコードでどこでエラーが発生したのかを見たいと思っています。 – TomTom

0

再入力して、あなたの例で私が持っている:

(defn extract [m] 
    (keys m)) 

(defn multiply [xs] 
    (mapv #(* 2 %) xs)) 

(defn process [xs] 
    (-> xs 
    (multiply)  ; seq -> seq 
    (extract)))  ; map -> seq ... fails ***line 21*** 

(println (process [1 2 3])) 
;=> java.lang.ClassCastException: java.lang.Long cannot be cast 
to java.util.Map$Entry, compiling:(tst/clj/core.clj:21:21) 

だから我々はextract方法に問題があることをファイルと行/ COL番号tst.clj.core.clj:21:21を言うれる例外では良い手がかりを得ます。

私が使用するもう1つの不可欠なツールは、「緩やかな」型チェックをクロージャに注入するPlumatic Sc​​hemaです。コードは次のようになります。エラーメッセージの形式は少し長いですしながら、

(ns tst.clj.core 
    (:use clj.core tupelo.test) 
    (:require 
    [tupelo.core :as t] 
    [tupelo.schema :as tsk] 
    [schema.core :as s])) 
(t/refer-tupelo) 
(t/print-versions) 

(s/defn extract :- [s/Any] 
    [m :- tsk/Map] 
    (keys m)) 

(s/defn multiply :- [s/Num] 
    [xs :- [s/Num]] 
    (mapv #(* 2 %) xs)) 

(s/defn process :- s/Any 
    [xs :- [s/Num]] 
    (-> xs 
    (multiply) ; seq -> seq 
    (extract))) ; map -> seq ... fails 

(println (process [1 2 3])) 

clojure.lang.ExceptionInfo: Input to extract does not match schema: 
[(named (not (map? [2 4 6])) m)] {:type :schema.core/error, :schema [#schema.core.One{:schema {Any Any}, 
:optional? false, :name m}], 
:value [[2 4 6]], :error [(named (not (map? [2 4 6])) m)]}, 
compiling:(tst/clj/core.clj:23:17) 

だから、それは我々が間違った型のパラメータを渡すおよび/または方法extractに形作ることをすぐに伝えます。あなたはこのような行が必要

注:

(s/set-fn-validation! true) ; enforce fn schemas 

私は特別なファイルtest/tst/clj/_bootstrap.cljを作成するので、同じ場所に常にあります。Plumaticスキーマの詳細については

参照してください。

あなたがclojure.spec使用することができます
+0

私は例外を除いて行番号を取得しませんでした。あなたは行番号を得るために物事をどのように設定しますか?私の投稿以来、私は仕様を見てきました。ある時点でClojureの標準となるものに焦点を当てるほうが賢明でしょうか? – TomTom

+0

Re行番号:私はreplをあまり使わないので、ファイルにすべてを入れ、 'lein test'を使います。 Re spec:Schemaは使いやすく、かなり普及していると思います。リングのようにほぼ「標準」です。 –

関連する問題