Skip to content

Instantly share code, notes, and snippets.

@tylerperkins
Created March 18, 2011 22:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tylerperkins/876955 to your computer and use it in GitHub Desktop.
Save tylerperkins/876955 to your computer and use it in GitHub Desktop.
The -> and ->> macros are enough!
;; Let's say you have a map recording the number of siblings for each individual:
(def num-sibs {:jack 0, :jill 2, :jane 0, :spot 7})
;; Say you want to find the number of only children. We can use ->>
(->> num-sibs
(map val)
(filter zero?)
count)
;; From the outset, we're treating the map as a sequence. But what if we need
;; to do object-like things to it instead of just sequence-like things. What
;; if I need to add my own entry, say [:me 0]:
(->> num-sibs
(assoc :me 0) ; Error!
(map val)
(filter zero?)
count)
;; This won't work, because assoc takes the map as its FIRST argument, not
;; its LAST as required by ->> . If we employed -> instead, we'd have a similar
;; problem with map and filter, which both take the sequence argument last. In
;; this particular example, the following works, however:
(-> num-sibs
(assoc :me 0)
(->> (map val)
(filter zero?)
count))
;; This isn't a very versatile technique, and it's getting messy! This issue
;; has prompted all kinds of complex ideas for threading the result through
;; an arbitrary argument. See
;; http://groups.google.com/group/clojure/browse_thread/thread/5582209f58ef11fb
;; and
;; http://groups.google.com/group/clojure/browse_thread/thread/66ff0b89229be894
;; But we'll see that adding another macro is unnecessary.
;;
;; I just want to write my succession of function calls with a minimum of new
;; abstraction and typing. We've been dismissing an obvious solution, maybe
;; for fear it would add some boilerplate. It's the idea of creating a new
;; function that does the job of assoc, but with rearranged arguments.
;; Thankfully, an annonymous function and Clojure's succinct syntax come to
;; the rescue:
(->> num-sibs
(#(assoc % :me 0)) ; Neat, this should have been obvious!
(map val)
(filter zero?)
count)
;; That's pretty clean. We've just added (# ... ) around the problematic call,
;; and placed % where the data should flow. Note that, since our closure has
;; just one argument, it works for both -> and ->>. Also, this applies to
;; situations where the data must flow through more than one argument, since
;; % can appear as many times in the clojure code as needed.
;;
;; It's a sure sign of an elegant language when time and again, the most
;; succinct solution requires no new tools!
;;
;; Tyler Perkins, 2011-03-18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment