The Rules of Transducer Club
First rule: you don’t call a transducer.
Second rule: only composition is allowed.
Third rule: better stateful than pure.
Here are the rationale behind each rule:
First rule: you don’t call a transducer.
Transducers may be so-called “stateful” that is they create stateful reducing functions. As a consequence you should not hold onto them for too long (otherwise state goes sour…). And there’s no betetr way to not hold them for too long that to never get a hold on them at all!
That’s why transduce
, sequence
, iteration
and chan
all take a transducer to avoid you the perils of having to call it.
Second rule: only composition is allowed.
It’s a direct consequence of the first rule: you should only compose transducers.
Third rule: better stateful than pure.
This one is a bit more subtle and caused exclusively by transduce
.
A reducing fn has now threes arities: [] -> acc
, [acc x] -> acc
and [acc] -> result
.
When one wants to pass state from one step to the next there are two options: be pure and pass it in the accumulator (and you’ll use the 1-arity to clean up) or be stateful.
It turns out that the pure option is a bad one, for two reasons. First reason, it’s going to increase object churn. Second reason, it’s going to change the type of the accumulator and this is a problem because:
- in
transduce
an init value may be specified and this init value must be of the type of the accumulator, - we don’t have a way to map from the result domain to the accumulator domain.
So it means the user has to be aware of an implementation detail of your transducer (the way you smuggle state in the accumulator) to craft a proper init value. It’s an abstraction leak, it’s bad. Be stateful.