Skip to content

Instantly share code, notes, and snippets.

@iantruslove
Last active January 20, 2018 02:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iantruslove/ebb391a1bc01143ececc5d3eff7b9ede to your computer and use it in GitHub Desktop.
Save iantruslove/ebb391a1bc01143ececc5d3eff7b9ede to your computer and use it in GitHub Desktop.
Clojure debugging: local setup

Clojure debugging: local setup

There are a couple of tools I set up here: some let debugging functions, and a neat library called Spyscope.

Due to how these are set up in your local ~/.lein profile, they're pervasively and conveniently available to you at the repl.

Inspecting let bindings

There are two variants: a simpler "debug let", and a richer "pprint let".

If you have a complex let you want to inspect:

(let [indices (get-indices-from-somewhere)
      clean-indices (filter clean-idx? indices)
      other-data (retrieve-data clean-indices)]
  (map :result other-data))

instead of doing the old _ (log/debug x) trick

(let [indices (get-indices-from-somewhere)
      _ (log/debug "indices" indices)
      clean-indices (filter clean-idx? indices)
      _ (log/debug "clean-indices" clean-indices)
      other-data (retrieve-data clean-indices)
      _ (log/debug "other-data" other-data)]
  (map :result other-data)) 
02:23:32 DEBUG [foo.core:74] - indices [{:idx 1, :state :ok} {:idx 4, :state :error} {:idx 22, :state :ok} {:idx 29, :state :ok}]
02:23:32 DEBUG [foo.core:76] - clean-indices ({:idx 1, :state :ok} {:idx 22, :state :ok} {:idx 29, :state :ok})
02:23:32 DEBUG [foo.core:78] - other-data ({:result {:town "Wimborne", :alternate "Bircham", :index 1}} {:result {:town "Erith", :alternate "Taplow", :index 22}} {:result {:town "Wimborne", :alternate "Vauxhall", :index 29}})

you can just replace the let with either dlet for a quick printout:

(user/dlet [indices (get-indices-from-somewhere)
            clean-indices (filter clean-idx? indices)
            other-data (retrieve-data clean-indices)]
  (map :result other-data))
indices : [{:idx 1, :state :ok} {:idx 4, :state :error} {:idx 22, :state :ok} {:idx 29, :state :ok}]
clean-indices : ({:idx 1, :state :ok} {:idx 22, :state :ok} {:idx 29, :state :ok})
other-data : ({:result {:town Calthorpe, :alternate Wrentham, :index 1}} {:result {:town Fairview, :alternate Wembley, :index 22}} {:result {:town Dovercourt, :alternate Vauxhall, :index 29}})

or with plet for a more detailed view:

(user/plet [indices (get-indices-from-somewhere)
            clean-indices (filter clean-idx? indices)
            other-data (retrieve-data clean-indices)]
  (map :result other-data))
{indices foo.core/indices,
 clean-indices
 ({:idx 1, :state :ok} {:idx 22, :state :ok} {:idx 29, :state :ok}),
 other-data
 ({:result {:town "Brocket", :alternate "Chailey", :index 1}}
  {:result {:town "Erith", :alternate "Amesbury", :index 22}}
  {:result
   {:town "Drayton Valley", :alternate "Torrington", :index 29}})}

I forget exactly where I found these now, they've been in my profile for so long.

Spyscope

Spyscope is a super-quick way of typing a bit of code to inspect some data. Same example as above, say you just wanted to look at the value of other-data before being mapped, and the result of that map:

(let [indices (get-indices-from-somewhere)
      clean-indices (filter clean-idx? indices)
      other-data (retrieve-data clean-indices)]
  #spy/p (map :result #spy/p other-data))

This shows up in the repl:

({:result {:alternate "Taplow", :index 1, :town "Fairview"}}
 {:result {:alternate "Bowden", :index 22, :town "Chigwell"}}
 {:result {:alternate "Hinton", :index 29, :town "Erith"}})
({:alternate "Taplow", :index 1, :town "Fairview"}
 {:alternate "Bowden", :index 22, :town "Chigwell"}
 {:alternate "Hinton", :index 29, :town "Erith"})

Magic.

Spyscope has three "functions", #spy/p for a quick printout, #spy/d for a more detailed debugging view with stacktrace and other cool stuff, and #spy/t for even more general purpose tracing.

Jan '18 setup

Right now, to get Spyscope working with Clojure 1.9, you need to clone the repo, lein install it, and depend on the [spyscope "0.1.7-SNAPSHOT"] dependency coordinate. Hopefully a new release is published to Clojars soon.

;; ~/.lein/profiles.clj
{:user {:source-paths ["/Users/ian/.lein/src"]
:injections [(require 'spyscope.core)]
:dependencies [[evalive "1.1.0"]
[spyscope "0.1.7-SNAPSHOT"]]
}}
;; ~/.lein/src/user.clj
(ns user
(:require [clojure.pprint]
[evalive.core :as evalive]))
(defmacro dlet [bindings & body]
`(let [~@(mapcat (fn [[n v]]
(if (or (vector? n) (map? n))
[n v]
[n v '_ `(println (name '~n) ":" ~v)]))
(partition 2 bindings))]
~@body))
(defmacro plet [bindings & body]
`(let ~bindings
(clojure.pprint/pprint (evalive/lexical-context))
~@body))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment