bryan maass

Created: 2016-10-25 Tue 16:39

Intoduction to tracks

Bryan Maass

Twitter @escherize
Clojurians Slack escherize
Telegram @escherize

tracks Rationale

  • The idea for this library was spawned by a discussion here at clj-syd!
  • What if functions could convey their intent at a glance?
  • Tracks is a library to create functions that transform maps, lists, and vectors from one shape to another.


I've been told one of these is necessary.



  • the external form or appearance characteristic of something; the outline of an area or figure
  • a thing that is difficult to see and identify clearly
  • a specific form assumed by something


  • make (something) fit the form of something else
  • give a particular shape or form to



  • a rough path or minor road, typically one beaten by use rather than constructed
  • a continuous line of rails on a railroad
  • a recording of one song or piece of music


  • follow the course or trail of (someone or something), typically in order to find them or note their location at various points

Gentle Intro

Do one thing, and do it well.

  • We know big maps are an issue.
  • Schema, Specter, Truss, Spec, Etc.
  • Tracks is a library to create functions that transform maps, lists, and vectors from one shape to another.
  • tracks on github


The study of where bugs come from. (?)

Stop being so clever

Brian Kernighan (co-author with Dennis Ritchie of the first book on the C Programming Language.)

"debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?"

  • Tracks aims to make many everyday transformations trivial, freeing you up to deal with inherent complexity.

Difficult to get an idea of exactly what data a program is using

Often in Clojure (and ClojureScript) we create functions that expect a certain shape of map, and return a new map.

(defn mystery [{:keys [a b] :as mysterious-map}]
  (-> mysterious-map
      (assoc-in [:c :d] a)
      (assoc-in [:e :f] b)))

This is concise, easy to test, and a pure function - All good.

However, all we can say about the input to mystery is that it has keys :a and :b. Then we can read through the function to see what operations are being done in a procedural way.

Here's an equivalent function on track.

(defn mystery [{:keys [a b] :as mysterious-map}]
  (-> mysterious-map
      (assoc-in [:c :d] a)
      (assoc-in [:e :f] b)))

;; vs

(def mystery
  (track {:a 1 :b 2}
         {:c {:d 1} :e {:f 2}}))


Hardcore destructuring:

(defn does-a-thing [{{:keys [x y z]} :b
                     {e :e {aca :a} :c} :a}]
  [x y z e aca])

(does-a-thing {:b {:x "x" :y "y" :z "z"}
               :a {:c {:a "aca"} :e "e"}})

;;=> ["x" "y" "z" "e" "aca"]

A little destructuring:

(defn does-a-thing [{:keys [a b]}]
  (let [{:keys [x y z]} b
        {:keys [c e]} a]
    [x y z e (:a c)]))

(does-a-thing {:b {:x "x" :y "y" :z "z"}
               :a {:c {:a "aca"} :e "e"}})

;;=> ["x" "y" "z" "e" "aca"]

on track:

(def does-a-thing
  (track {:b {:x 1 :y 2 :z 3}  ;;<--- [1]
          :a {:c {:a 5} :e 4}}
         [1 2 3 4 5]))

(does-a-thing {:b {:x "x" :y "y" :z "z"}
               :a {:c {:a "aca"} :e "e"}})

;;=> ["x" "y" "z" "e" "aca"]

;;; [1] note we can use any scalar value for leafs in the structure

What's next for track?

  • Klipse blog post (coming soon) With interactive editable examples!
  • Automatic generation of doc strings for the functions produced by track
  • One-to-many track functions

    (defn one-to-many (tracks {:a 1} {:b 1 :c 1}))
    (one-to-many {:a "Hello"})
    ;;=> {:b "Hello" :c "Hello"}
  • Optional strict transformations only i.e. throwing errors when the wrong shape is passed in.
(defn just-takes-a (tracks {:a 1} {:b 1}))

(one-to-many {:a "Hello" :b "???"})

;; EXCEPTION: just-takes-a callled with {:a "Hello" :b "???"}
;;            but requires a map with paths: [[:a]]


Thanks clj-syd!



Created by Bryan Maass.