Good post. Question for people who’ve used them: what makes transducers so great? For me, they’re a bit like monads in that it’s not hard to understand “what they are”, but I still haven’t used them enough to get a sense of what makes them so powerful.
I agree that threading macros often make many transformations easier to read. Although purely functional and lazy code might be argued to be orderless what we call f . g is usually conceptually “g and then f”. Although this “backward” notion is easy once you’re fluent, I think that it is another stumbling block when we’re trying to sell people on the way we do things.
f . g
what makes transducers so great?
They make it easier to reason about the transformation of the data as a process, irrespective of the specific data structure. So in that sense I guess they are kinda like monads because they abstract away from the data specifics. Transducers are also very efficient, in most cases they approach the kind of performance that you would get with hand-optimized code.
If you’re a Haskeller, this article explains transducers using Haskell code and types. Even though I’m better at Clojure (my day-job language) than Haskell, I found this explanation more helpful than reading Clojure code, for example cgrand’s xforms can get really complicated.
That example for the threading macro could go either way, I certainly wouldn’t consider it crappy.
Definitely possible to go overboard with threading. I often find it neatest to thread certain subexpressions in an expression that could otherwise be fully threaded because it makes the ‘core’ semantics of the operation clearer.