0

I am still new to functional programming and have been trying to learn how to use transducers. I thought I had a good use case but every time I attempt to write a transducer with Ramda for it, I get the following error:

reduce: list must be array or iterable

I have tried rewriting it several ways and looked at several explanations on the web of transduction but to no avail. Any suggestions?

const data = [{cost:2,quantity:3},{cost:4,quantity:5},{cost:1,quantity:1}];
const transducer = R.compose(R.map(R.product), R.map(R.props(['cost', 'quantity'])));
const result = R.transduce(transducer, R.add, 0)(data);
console.log(result)
webstermath
  • 555
  • 1
  • 5
  • 14

2 Answers2

3

In the context of a transducer, compose reads left to right. You just need to invert product and props:

const data = [
  {cost:2,quantity:3},
  {cost:4,quantity:5},
  {cost:1,quantity:1}];
  
const transducer = 
  compose(
    map(props(['cost', 'quantity'])),
    map(product));

console.log(

  transduce(transducer, add, 0, data)

)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {compose, map, props, product, transduce, add} = R;</script>
customcommander
  • 17,580
  • 5
  • 58
  • 84
  • Thanks, it worked. Now does that mean that since the order had to be reversed that transduce executes a transducer in reverse order? – webstermath Jul 16 '19 at 05:56
  • 1
    Nope it doesn’t. The computation will run in the order as expressed in compose. There’s a good reason why compose is “inverted” in a transducer but I don’t remember it. I’m sure other people will provide additional information – customcommander Jul 16 '19 at 06:16
2

The reason why the order reverses is that transducers utilize a property of function composition that is sometimes called abstraction from arity. It simply means that a function composition can return, well, another function:

const comp = f => g => x => f(g(x));

const mapTrace = tag => f => (console.log(tag), xs => (console.log(tag), xs.map(f)));

const sqr = x => x * x;

const main = comp(mapTrace("a")) (mapTrace("b")) (sqr); // returns another function

console.log(main); // logs the 2nd map and then the 1st one (normal order)

// pass an additional argument to that function

console.log(
  main([[1,2,3]])); // logs in reverse order

Why returns the composition another function? Because map is a binary function that expects a function argument as its first argument. So when the composition is evaluated it yields another compositon of two partially applied maps. It is this additional iteration that reverses the order. I stop at this point without illustrating the evaluation steps, because I think it would get too complicated otherwise.

Additionally, you can see now how transducers fuse two iterations together: They simply use function composition. Can you do this by hand? Yes, you can absolutely do that.