I'm trying to write a debug macro that prints out expressions and their values. This lead to problems if I send in a lazy-seq, because if I turn it into a string (with str) the program hangs. It's easy to detect a lazy-seq if it's at the toplevel:
(def foo (cycle [1 2]))
(= (type foo) clojure.lang.LazySeq) ;=> true
But of course if it's nested inside another collection this doesn't work
(def bar (list (cycle [1 2])))
(= (type bar) clojure.lang.LazySeq) ;=> false
To deal with this I would need one of two things:
1: A function which checks a collection too see if it contains a lazy-seq nested somewhere.
2: A function to turn a collection into a string without evaluating nested lazy-seqs, something like this:
(str2 {:inf (cycle [1 2])}) => "{:inf #clojure.lang.LazySeq@e9383}"
Using Michał Marczyk's answer I came up with this macro:
(defmacro dbg-print [& rest]
"Print out values or expressions in context"
`(let [lazy-take# 5 ;when printing lazy-seq, how many elements to print
symb-str# (map str '~rest)
symb-evl# (reverse
(binding [*print-length* 10]
(loop [coll# (list ~@rest) retur# '()]
(if (not (empty? coll#))
(recur (rest coll#) (cons (pr-str (first coll#)) retur#))
retur#))))
pairs# (map #(str %1 %2 %3 %4) symb-str# (repeat ":") symb-evl# (repeat " "))
str# (reduce str pairs#)]
(println (format "%s\n" str#))))
It works like this:
(dbg-print (+ 1 3) (cycle [1 2])) ;=> (+ 1 3):4 (cycle [1 2]):(1 2 1 2 1 2 1 2 1 2 ...)
And can handle nested lazy-seqs:
(dbg-print (list (cycle [1 2]))) ;=> (list (cycle [1 2])):((1 2 1 2 1 2 1 2 1 2 ...))