Clojure juice

2 thoughts
last posted April 27, 2016, 1:54 p.m.
0

Can’t use -> in update-in (clojuredocs). I.e., if you’re doing this in the REPL:

(-> my-map :a-path :b-path first :c-path second),

to get to a certain path in a map and then apply some function to it (in this case, first, :c-path, and second), and now want to update that path using that function, you’ll need:

(update-in my-map [:a-path :b-path] #(-> % first :c-path second)),

or alternatively,

(update-in my-map [:a-path :b-path] (comp second :c-path first)),

noting how with comp you reverse the order of functions from ->.


I mention this because I prefer using partial or apply or comp to the function literal #()—mainly because I find myself often needing to nest function literals inside function literals, which isn’t allowed—but in this case, because of the reverse-notation-requirement of comp, I preferred the #(-> % …) notation.


It takes some getting used to, these idioms in Clojure—I’ve thought of programming idioms as the preferred way to do something among other options, but here idiom means something quirky that works while plausible-seeming alternatives don’t. And it’s taken a while but there’re a couple of other idioms around how to compose functions: around -> and ->>.

And I love these threading operators to death: in 1530 lines of Clojure (via cloc), these operators appear 117 times (via $ grep -rsn -- "->" * | wc -l), so a little more than ten lines of code between uses of -> and ->>. I don’t use them to thread just one function, but that’s a lot of usage, and I take note because I have no idea how readable these two constructs are down the road or to other developers.

I use them in code because it’s easy to write (in REPL & in the editor). But it’s only easy because I’ve worked out some idioms for their usage and how to convert from one to the other.

E.g., often one uses threading macros and gets a coll (seq, vector, etc.) that needs to be map (or keep or filter &c.). If you were using ->>, you’re fine: (->> [1 2 3] (map inc)) ;✓ . But if you were using ->, perhaps because you were using nth or clojure.string/index-of or some other function that takes the “main variable” as first argument, then you’re in for

  • (-> [1 2 3] (#(map inc %)))
  • (-> [1 2 3] ((partial map inc))) ; note nesting of partial!
  • (-> [1 2 3] (->> ,,, (map inc ,,,))) ; ,,, show threaded positions

All these alternatives smell like they’re going to be trouble for me later, but for now, I’m charging ahead using them because threading macros make coding faster.


Postscript. I think some of the weirdness & contortions needed above would go away if Clojure had right-partials. But given Clojure’s commitment to multi-methods and variable argument functions (which was one plausible reason given online), it doesn’t exist and its use would be prone to later regret.

1 later thought