Because you apply the composed transducers (f,g,h) to a reducer (r), the leftmost (f) becomes topmost, first to act on the eventual arguments (acc, el) to the augmented reducer f( g(h(r)) ):(*)
compose(f,g,h)(r)(acc, el) =~= f( g( h( r)) )(acc, el)
<--------
--...........->
Indeed the rightmost, h, is applied to the argument r first; but that r is also a function; the whole composed thing is a function; and the composed function has f as its first component, so it is f that gets to work on the next arguments (acc, el) first, using the result of the inner composition g( h( r)) according to the definition of f.
What we have here, is
r // reducer
h // transducer
h( r ) // reducer
g // transducer
g(h( r )) // reducer
f // transducer
f(g(h( r ))) // reducer
(*) So you have defined your functions so that
mapReducer(foo)(reducer)(acc, el)
=~= reducer(acc, foo(el));
filterReducer(predicate)(reducer)(acc, el)
=~= predicate(el) ? reducer(acc, el) : acc;
concatReducer(acc, el)
=~= acc.concat(el);
and, most importantly,
compose(f,g,h,...,p,q)(r)
=~= [f,g,h,...,p,q].reduceRight((acc, fn) => fn(acc), r);
=~= f(g(h(...(p(q( r ))...)))
So then
[1, 2, 3]
.reduce(
compose(
f, // f = mapReducer(times2),
g, // g = filterReducer(isGreaterThanThree),
h // h = mapReducer(add1),
)(r), // r = concatReducer
[]
)
=~= [1, 2, 3] .reduce( f(g(h(r))), []) // rc = f(g(h(r)))
=~= rc( rc( rc( [], 1), 2), 3)
=~= rc( rc( f(g(h(r)))( [], 1), 2), 3)
=~= rc( rc( mapReducer(times2)(g(h(r)))( [], 1 ), 2), 3)
// mapReducer(foo )(reducer)(acc, el)
// =~= reducer( acc, foo(el))
=~= rc( rc( g(h(r))([], times2( 1)), 2), 3)
=~= rc( rc( filterReducer(isGreaterThanThree)(h(r))([] , times2( 1)),
2),
3)
// filterReducer(pred )(reducer)(acc, el)
// =~= pred(el) ? reducer( acc, el) : acc
=~= rc( rc( isGreaterThanThree( twice1) ? h(r)( [], twice1) : [],
2), /* where twice1 = times2(1)
h = mapReducer( add1) */
3)
=~= rc( rc( isGreaterThanThree( twice1) ? r([], add1(twice1)) : [],
2), /* where twice1 = times2(1)
r = concatReducer */
3)
=~= rc( rc( isGreaterThanThree( twice1) ? [].concat(add1(twice1)) : [],
2), /* where twice1 = times2(1) */
3)
=~= ...
and we can see that mapReducer(times2) gets to work on the list's elements first, before the filtering by isGreaterThanThree and then the mapping of add1.
In processing the [1,2,3], first the mapping of times2 is done (implicitly as-if making it [2,4,6]), then the filtering (which would leave just [4,6]), and then the mapping of add1, for the final result of [5,7] indeed.
There's no creating the interim structure though. Instead one composite reducer is built, and used by one reduce, advancing along the input one step at a time.
Thus transducers are a fusion / deforestation technique. Intuitively, nested folds should fuse, eliminating the need for the interim structure creation by an inner fold only so it can be consumed by the wrapping fold; following the transducer discipline lets us achieve just that.
So then, mapReducer and filterReducer are badly named. They are actually mappingTransducer_Maker (or just mapping) and filteringTransducer_Maker (or just filtering), where
mapping(foo) is a mapping transducer that applies foo to the input's element elt, resulting in foo(elt), and
filtering(pred) is a filtering transducer that filters out the input's element el according to the predicate call pred(el), or keeps it in.
Or rather, a transducer augments its argument reducer so that when the combined, augmented reducer is finally called with the accumulator and the current element, it manipulates the element and the accumulator in a prescribed way before passing the results to the base reducer:
mapping(foo)( filtering(pred)(reducer) )( acc, elt)
----------------------------------------( )
=~=
filtering(pred)(reducer)( acc, foo(elt) )
=~=
pred( foo(elt) ) ? reducer( acc, foo(elt) ) : acc
So, mapping(foo) and filtering(pred) are composed right-to-left with respect to their argument, reducer; but then the composed reducer starts working from the top, tracing the transducers' effects left-to-right -- first doing the mapping, and doing the filtering after that.