Exploring patterns as a means of documenting Clojure functions to aid recall and understanding.
Whats the difference in Clojure between:
All non-side effecting functions create or alter a pattern. To explain a function’s pattern we use a number of descriptions.
- A function signature:
(nthrest coll n)
- A textual description:
- Examples showing application of the function:
Exploration vs Recall
As someone with a brain far more orientated to visuals than text I struggled to remember and understand many Clojure functions:
The documentation of patterns in the Clojure documentation is all text. Even with the documentation brought into the editor I struggle. Example from Cider & Emacs:
Clojure has a strong focus on REPL driven development. If you don’t understand a function use an interactive REPL to explore examples.
Critically this favours discovery over recall. I can never remember the difference between
cons, but I can find out through the REPL.
To help aid memory and understanding I’ve turn the examples of the collection orientated functions in Clojure into visual patterns. I won’t try and make any general case on visuals vs text (Its a fuzzy research area: http://studiokayama.com/text-vs-visuals/).
Patterns for the visual brain
Applying functions to collections of data using: https://github.com/josephwilk/functions-as-patterns
Return a seq of all but the last item in coll, in linear time
(concat x y)
Returns a lazy seq representing the concatenation of the elements in the supplied colls.
(conj coll x) (conj coll x & xs)
conj[oin]. Returns a new collection with the xs 'added'. (conj nil item) returns (item). The 'addition' may happen at different 'places' depending on the concrete type.
(cons x seq)
Returns a new seq where x is the first element and seq is the rest.
Returns a lazy sequence removing consecutive duplicates in coll. Returns a transducer when no collection is provided.
Returns a lazy sequence of the elements of coll with duplicates removed. Returns a stateful transducer when no collection is provided.
(drop-last n coll)
Return a lazy sequence of all but the last n (default 1) items in coll
drop-last 2 )
Takes any nested combination of sequential things (lists, vectors, etc.) and returns their contents as a single, flat foldable collection.
(interpose sep coll)
Returns a lazy seq of the elements of coll separated by sep. Returns a stateful transducer when no collection is provided.
(interleave coll coll)
Returns a lazy seq of the first item in each coll, then the second etc.
(nthnext coll n)
Returns the nth next of coll, (seq coll) when n is 0.
(nthrest coll n)
Returns the nth rest of coll, coll when n is 0.
nthrest 2 )
(partition n coll)
Returns a lazy sequence of lists of n items each, at offsets step apart. If step is not supplied, defaults to n, i.e. the partitions do not overlap. If a pad collection is supplied, use its elements as necessary to complete last partition upto n items. In case there are not enough padding elements, return a partition with less than n items.
partition 3 )
(partition-all n coll)
Returns a lazy sequence of lists like partition, but may include partitions with fewer than n items at the end. Returns a stateful transducer when no collection is provided.
partition-all 3 )
(replace smap coll)
Given a map of replacement pairs and a vector/collection, returns a vector/seq with any elements = a key in smap replaced with the corresponding val in smap. Returns a transducer when no collection is provided.
replace [0 3 4] )
Returns a possibly empty seq of the items after the first. Calls seq on its argument.
Returns a seq of the items in coll in reverse order. Not lazy.
Return a random permutation of coll.
Returns a sorted sequence of the items in coll. If no comparator is supplied, uses compare. comparator must implement java.util.Comparator. Guaranteed to be stable: equal elements will not be reordered. If coll is a Java array, it will be modified. To avoid this, sort a copy of the array.
(take-nth n coll)
Returns a lazy seq of every nth item in coll.
take-nth 3 )
(split-at n coll)
Returns a vector of [(take n coll) (drop n coll)]
split-at 2 )
As someone who performs live coding to an audience I perhaps have a different value on recall vs exploration. Hundreds of eyes staring at you tends to have that effect.
While some examples are stronger through patterns than others, at least for myself the use of a visual aid as part of development and documentation is beneficial. Its the only way I can the remember the oddity of
Within my REPL interaction I use the functions-as-patterns toolkit, providing a higher level representation of the patterns and data. I can understand a drum pattern faster through colour than I can through a 1 & 0 data structure.
In creating the cheatsheet the value of the comparison of functions through patterns also became clear. I discovered almost identical functions such as
nthrest which only differed in a special case (with an empty sequence).
Problems of turning data into colour
While this visual cheatsheet is useful there are caveats:
Semantic’s of arguments
Its not always clear if an argument is an index or a value. If we look at the
replace function example:
(replace (take 5 (hues-of-color)) [0 3 4])
0,3 & 4 are references to indices within the first argument. Ideally it would be nice to replace those with the relevant colour.
However the functions-as-patterns library cannot tell these are not values, it assumes everything is a value. Hence you end up with
[0 3 4] drawn in shades of black:
Pattern of emptyness
I’ve not tried to visually represent,
nil. Some functions are defined by the difference in handling the empty case. The patterns might mis-lead you to think
nthrest are identical when they are not.
What type is a square?
Clojure has multiple types of sequences,
lazy-seq ,etc. To keep the visual pattern simple I’ve not represented these types.
Functions applying functions
I’ve purposely skipped the
filter functions as they tend to mix two patterns together. That of the core function and the applied function. The value of the patterns gets lost.
Brains seek patterns.
Do you see a pattern in the randomness? A single example might reveal a false pattern. Many examples would be required to re-enforce the randomness of the resulting patterns. Example
Colours don’t always imply a logical order. Example