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 with rename.

  • 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 the anonymous-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-string

(fn-string f)

str applied to the result of fn-symbol.

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):

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]}"