Skip to content
Jonathan Claggett edited this page Apr 10, 2015 · 3 revisions
  1. Require SynThread with the alias -> like this in Clojure:

    (ns your.ns.here
        (:require [lonocloud.synthread :as ->]))

    or like this if you use Clojurescript:

    (ns your.ns.here
        (:require [lonocloud.synthread :as -> :include-macros true]))
  2. Always start a threaded block with Clojure's standard -> macro or ->/do (see next point). This clearly identifies the topic to be threaded.

    ;; good
    (-> {:first "John" :last "Smith"}
        (->/update :last first)) ;; replace last name with last initial
    
    ;; bad
    (->/update {:first "John" :last "Smith"}
                :last first) ;; replace last name with last initial
  3. Don't change the type (or shape) of the topic as it flows through. In our experience, the threading macros are used to either dig into a deep data structure or are used to build a result by describing a pipeline of operations. The main difference between digging and building is that the type and shape of the threaded topic is changing or is constant respectively. We use synthread macros for building as a general rule.

    ;; good
    (-> {:a 1 :b 2}
        (->/update :a inc)
        (->/in [:b]
          (->/for [n (range 3)]
            (+ n))))
    ;; returns {:a 2 :b 5}
    
    ;; bad
    (-> {:a 1 :b 2}
        (->/update :a inc)
        :b ;; type changed from map to number
        inc)
    ;; returns 3
  4. Use ->/as to put the threaded value (or "topic") into a named local variable. This is useful when you need to call a function or macro that needs access to the topic in some parameter slot other than the first:

    (-> {:a 1 :b 2}
        (->/update :a inc)
        (->/as topic                             ;; label topic
        (->/when (> (:b topic) 10)
            (assoc :large-b true))))

    Standard destructuring is supported by ->/as:

    (-> {:a 1 :b 2}
        (->/update :a inc)
        (->/as {:keys [b]}                       ;; destructure topic
        (->/when (> b 10)
            (assoc :large-b true))))

    Additionally, a special destructuring form is supported allowing the use of functions. Passing a threaded form will implicity insert the topic at the front and use the last argument as the binding label. For example:

    (-> {:a 1 :b 2}
        ;; use of a function while destructuring
        (->/as (-> vals (->/apply max) max-val)
        (->/when (> max-val 10)
            (assoc :large-val true))))
  5. Clojure's do and doto macros are useful in these threading contexts, so don't be afraid to use them. do lets you stop threading, and yet pass a result to the next threaded step:

    (-> {:a 1 :b 2}
        (->/update :a inc)
        (->/when we-should-reset?
        (do {:a 0 :b 0}))  ;; see also ->/reset function
        (->/update :b inc))

    This can be particularly useful in conjunction with ->/as:

    (-> {10 :orig, 20 :orig}
        (->/as topic
        (do
            (reduce #(assoc %1 %2 :default) topic (range 5)))))

    On the other hand, doto is nice when you do not want to pass a result to the next step:

    (-> {:a 1 :b 2}
        (doto prn)
        (->/update :a inc))

    The debugging prn above works, but the patten rapidly becomes awkward if you want to provide a label to the prn. That would actually require a combination of ->/as to label it and do to prevent the topic from being printed before the label because of threading. This is exactly the purpose of ->/aside:

    (-> {:a 1 :b 2}
        (->/aside topic        ;; note the body is entirely unthreaded
        (prn :topic topic)
        (println "Note: b is currently" (:b topic))) ;; return is ignored
        (->/update :a inc))
  6. In addition to the threading macros, two helper functions are also available: ->/reset and ->/apply. Use ->/reset to set the value of the threaded topic (similar to Clojure's reset!) and use ->/apply to call a function with the threaded topic as its first argument (similar to Clojure's apply).

    ;; example of ->/reset
    (-> false
        (->/reset true))
    ;; returns true
    
    ;; example of ->/apply
    (-> [0]
        (->/apply conj [1 2 3])  ;; => (conj [0] 1 2 3)
        (->/apply [conj 4 5 6])) ;; also works!
    ;; returns [0 1 2 3 4 5 6]
Clone this wiki locally