Example code:
// Compose functionality
const compose = (...fns) => {
return args => {
return fns.reduceRight((arg, fn) => fn(arg), args);
}
};
// List of transformation and predicate functions
const add1 = x => x + 1;
const isGreaterThanThree = x => x > 3;
const times2 = x => x * 2;
// Concat and Sum reducers (or the thing that I want to build).
/*
In this case, I'm using concatReducer, but I can easily substitute concatReducer with
sumReducer and change the initial value of the reduce method to zero.
*/
const concatReducer = (acc, el) => acc.concat(el);
const sumReducer = (acc, el) => acc += el;
// Transformation reducer (not sure the appropriate terminology)
const mapReducer = transform => {
return reducer => {
return (acc, el) => {
return reducer(acc, transform(el));
}
}
};
// Predicate reducer (again, not sure the appropriate terminology here)
const filterReducer = predicate => {
return reducer => {
return (acc, el) => {
return predicate(el) ? reducer(acc, el) : acc;
}
}
}
[1, 2, 3]
.reduce(
compose(
mapReducer(times2),
filterReducer(isGreaterThanThree),
mapReducer(add1),
)(concatReducer),
[]
);
I expect the value to be [ 8 ] instead of [ 5, 7 ].
Compose is a right-associative (reduceRight), but in this case, it is behaving as left-associative.
I thought to myself that maybe my compose function implementation was wrong. As a result, I pulled in ramda.js and used R.compose, but I got the same result.
Am I doing something wrong? Or is this one of those scenarios that compose is left-associative when dealing with transducers?
Quotes and some of the examples are taken from https://github.com/cognitect-labs/transducers-js.
What is a transducer?
Example: (adapted)
Note: we'll cover
Mapperlater.Let's rewrite with some arrow functions:
In the snippet above it should be clearer that a transducer is indeed a function that takes a transformer and returns another transformer. These two compositions are equal:
Fun fact
The function composition now waits for the initial transformer which is commonly referred to as the "step" transformer (or "step" function) which is responsible for accumulating the transformation into a container.
A typical container would be either an array, a string or an object. Such "step" transformer would either:
Pushin the rest of this answer)But we don't need to know the details of such transformer. However we do need to understand what transformers are.
What is a transformer?
Example: (adapted)
In order to understand why transducers reverse the order of the composition, we need to take a closer look at the
@@transducer/stepmethod:Note:
resultis the container into which we will accumulate the transformations.When you do:
You end up with a final transformer that looks like this:
Usually your library will do this for you but for educational purpose we will call the the
@@transducer/stepmethod on that final transformer and decompose the functions calls:Is similar to:
Even though we did
compose(mapper(double), mapper(inc))we can see that thedoublefunction got applied beforeincdid. This isn't a bug in thecomposefunction, it is simply how transformers are supposed to work when composed together.