Clojure 開発におけるデバッグまとめ.
refs: 📝Clojure DX 📝Clojure REPL Driven Development
printデバッグ
古代人から現代まで引き継がれている由緒正しきデバッグ手法, aka. わたしの得意技.
print/println
Clojureでもっとも有名な標準出力関数.
pr/prn/pr-str
オブジェクトの内容をいい感じに出力してくれる.
(println “おれはここだよ”)のようなトレースデバッグにはprintlnをつかい, データの中身をみる(Inspectする)にはprnをつかうのが使い分け.
str
clojure.pprint
Pretty Print for Clojure.
clojure.pprint namespace | ClojureDocs.
(require '[clojure.pprint/pprint :as pp])
pprint: いい感じに出力.
prnでオブジェクトを表示すると, たとえば大きなネストしたMapは1行に表示されて見にくい.その場合は, clojure.pprint.pprintをつかうと見やすくなる.
REPLで (pp) と評価すると最終評価結果が表示される. このppの便利な使い方は, 普通に標準出力した結果が複雑だったときに, 再度ppを通じて表示することでさっきのデータ構造がいい感じになる.
clojure.pprint/print-table
Mapのコレクションをいい感じに表で表示してくれる.
https://clojuredocs.org/clojure.pprint/print-table
print-method
Javaのクラスによく自分で実装するtoStringメソッドがClojureにもほしい!
-> print-method というmultimethodをつかう.
toStringをオーバーライドするとJavaのクラスをSystem.out.printlnするときにクラスの内容表示を自分でカスタマイズできる.
clojureでは print-methodというmultimethodが定義されている.
ただし, 基本的にはClojureはデータをMapで扱うことが推奨されているので, そもそもいらないかもしれない. JavaでtoStringが必要だったのはオブジェクトの中身を見る必要があるから. Clojureなら直接Eval!
hashp
https://github.com/weavejester/hashp
hashpをつかうと spy的に変数の内容を補足できる.
コレは大変べんり!
clojure.inspector
Java UIライブラリを使ったデータの描写. 便利らしいが私の環境で動かない…
- inspect
- inspect-table
- inspect-tree
ref. Clojure Inspector · Practicalli Clojure
🔧clojure.core/tap
Clojure1.10から導入された機能. Clojure/ClojureScript両方つかえる.
- add-tap で出力先を指定.
- tap> で出力するデータを指定.
- remove-tapで出力先を削除.
tap自体はEditorやREPLから使うのだが, これと連携するツールが便利. GUIによるデータの可視化が可能になる.
refs. clojure tap - clojure.org
- Cognitect REBL
- Site
- Datomic開発元でおなじみCognitectが開発している.
- OSSではないものの Stuart HallowayとCognitectのサポートは強そう.
- Reveal
- GitHub
- Read Eval Visualize Loop for Clojure
- OSSであり REBLの対抗馬.
- REPLのように振る舞ういVM上のペインでDataを可視化.
- つまり起動するとJavaアプリとして立ち上がる.
- Portal
- GitHub
- Web技術で開発されている.
- つまり起動するとChrome PWAアプリとして立ち上がる.
- revealより後発なので機能的にはrevealが先行している.
- UIがrevealよりもイケイケ感がある.
- (revealはEclipse感があり若干のダサさがある).
Instpect Data Structure
prnでデータ構造をとりあえずREPL出力(printlnは文字列表示用).
clojure.pprint/pprintでよりきれいな表示.
表形式(list of maps)は, clojure.pprint/print-tableがよりよい.
🔨tech.ml.datasetはprint-tableよりもさらに見やすい.
Portal
以下は portal でintegrantの状態を表示.
tapについてはだいたい他も同じ.
(require '[portal.api :as p])
(require '[integrant.repl.state :refer [system]])
(def p (p/open)) ; Open a new inspector
(add-tap #'p/submit) ; Add portal as a tap> targe
(tap> system)
(p/clear) ; Clear all values
(remove-tap #'p/submit) ; Remove portal from tap> targetset
(p/close) ; Close the inspector when done
apiの結果を叩くごとに更新するにはこう.
;; 初期化
(def d (p/open))
;; tapの代わりにreset!でデータを挿入.
;; するとportal UIに反映される.
(reset! d (get-product {:cid "ssis00335"}))
;; Portal UIではなく REPLで表示
@d
;; swap!で値の更新.
(swap! d (constantly (get-product {:cid "ssis00333"})))
Namespace探訪
ns-publicsをつかうと, namespaceに定義されている変数を見ることができる.
(ns-publics 'eda)
M-x cider-browse-nsでも同様なことができるが, cider-inspecterと連携するには直接ns-publicsを評価したほうがいいかも.
see more: ✅namespaceにbindingsされたシンボルを確認するには?
定義を消したいときはM-x cider-undefがとても便利.
see more. ✅namespaceから定義を取り除く(cider-undef)
logging
ref: Clojure: Logging
Clojure REPLエラーメッセージまとめ
REPLで評価したときに出てくるエラーメッセージの分類とその対処.
CIDERにおけるerror-buffer
エラーメッセージは cider-error bufferに出力される.
stack-frameはデフォルトでたくさんでてくるが, filterをつかうと見やすくなる.
(setq cider-stacktrace-default-filters '(tooling dup))
;; or
(setq cider-stacktrace-default-filters '(project))
Unhandled clojure.lang.ArityException
関数呼び出し時の引数の数が違う.
- Unhandled clojure.lang.ArityException Wrong number of args (0) passed to: xxxx
Topics
Clojureメモリ枯渇/メモリリーク(OOME)調査
基本的にはJVMのノウハウをそのままつかう. 📝JVM:OutOfMemoryError(OOME).
経験則だが, atomを宣言するとその値に対してGCで領域が解放されない. そのため, うっかりloopのなかで繰り返しatomをつかって値を作り続けるとそれがメモリ枯渇を導いた.
または, 無限遅延シーケンスに対してリミットを定めずにシーケンスに値を追加し続けるとメモリ枯渇する. とくにプロダクト投入ではbuffer sizeを設定しておくことは無難.
nREPLを再起動せずにパッケージを追加したい
https://github.com/clj-commons/pomegranate
どうもnREPLにバグがあるようでわたしの環境で実行するとハングし続けたので使うのをやめた. (<2022-08-07 Sun 08:18>
)