Created
March 18, 2011 22:18
-
-
Save tylerperkins/876955 to your computer and use it in GitHub Desktop.
The -> and ->> macros are enough!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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