such.readable
Stringify nested structures such that all functions - and particular values of your choice - are displayed in a more readable way. value-string and fn-symbol are the key functions.
default-function-elaborations
Anonymous functions are named fn
and functions are surrounded by <>
elaborate-fn-symbol
(elaborate-fn-symbol f {:keys [anonymous-name surroundings]} anonymous-names)
(elaborate-fn-symbol f)
A more customizable version of fn-symbol. Takes f
, which must be a function or multimethod. In all cases, the return value is a symbol where f
’s name is embedded in the surroundings
, a string. For example, if the surroundings are “<!!>”, a result would look like <!cons!>
.
f
’s name is found by these rules, checked in order:
f
has had a name assigned withrename
.f
is a key in(deref anonymous-names)
. The value is its name.The function had a name assigned by
defn
,let
, or the seldom used “named lambda”:(fn name [...] ...)
. Note that multimethods do not have accessible names in current versions of Clojure. They are treated as anonymous functions.The function is anonymous and there are no other anonymous names. The name is
anonymous-name
, which is also stored in theanonymous-names
atom.After the first anonymous name, the names are
<anonymous-name>-2
<anonymous-name>-3
and so on.
In the single-argument version, the global or default elaborations are used, and anonymous-names
is empty. See set-function-elaborations!.
fn-symbol
(fn-symbol f)
Transform f
into a symbol with a more pleasing string representation. f
must be a function or multimethod.
(fn-symbol even?) => '<even?>
(fn-symbol (fn [])) => '<fn>
(fn-symbol (fn name [])) => '<name>
(let [foo (fn [])] (fn-symbol foo)) => '<foo>
See elaborate-fn-symbol for the gory details.
forget-translations!
(forget-translations!)
There is a global store of translations from values to names, used by with-translations and value-strings. Empty it.
instead-of
(instead-of value show)
Arrange for value-string to show value
as show
. show
is typically a symbol, but can be anything.
pathcache4291
pathcache4303
rename
(rename f name)
Produce a new function from f
. It has the same behavior and metadata, except that fn-symbol and friends will use the given name
.
Note: f
may actually be any object that allows metadata. That’s irrelevant to fn-symbol
, which accepts only functions, but it can be used to affect the output of value-string.
set-function-elaborations!
(set-function-elaborations! {:keys [anonymous-name surroundings], :as all})
Control the way functions are prettified. Note: this does not override any value changed with with-function-elaborations
.
(set-function-elaborations! {:anonymous-name 'anon :surroundings ""})
value
(value x)
Like value-string, but the final step of converting the value into a string is omitted. Note that this means functions are replaced by symbols.
value-string
(value-string x)
Except for special values, converts x
into a string as with pr-str
. Exceptions (which apply anywhere within collections):
If a value was given an alternate name in with-translations or instead-of, that alternate is used.
Functions and multimethods are given better names as per fn-symbol.
Examples:
(value-string even?) => "<even?>"
(value-string {1 {2 [even? odd?]}}) => "{1 {2 [<even?> <odd?>]}}"
(instead-of even? 'not-odd)
(value-string {1 {2 [even? odd?]}}) => "{1 {2 [not-odd <odd?>]}}"
(def generator (fn [x] (fn [y] (+ x y))))
(def add2 (generator 2))
(def add3 (generator 3))
(value-string [add2 add3 add3 add2]) => "[<fn> <fn-2> <fn-2> <fn>]"
(def add4 (rename (generator 4) 'add4))
(def add5 (rename (generator 4) 'add5))
(value-string [add4 add5 add5 add4]) => "[<add4> <add5> <add5> <add4>]"
with-function-elaborations
macro
(with-function-elaborations {:keys [anonymous-name surroundings], :as all} & body)
Change the function elaborations, execute the body, and revert the elaborations.
(with-function-elaborations {:anonymous-name 'fun :surroundings "{{}}"}
(fn-symbol (fn []))) => {{fun}}
with-translations
macro
(with-translations let-style & body)
Describe a set of value->name translations, then execute the body (which presumably contains a call to value-string).
(with-translations [5 'five
{1 2} 'amap]
(value-string {5 {1 2}
:key [:value1 :value2 5]}))
=> "{five amap, :key [:value1 :value2 five]}"