Improve Composition with the Compose Combinator

Paul Frend
InstructorPaul Frend

Share this video with your friends

Send Tweet

To make our composition more readable and easier to name we are going to ceate a compose function we can use to avoid having to manually nest our transducer calls.

We'll also go over semantically naming your compose functions for extra readability.

Stephen James
Stephen James
~ 3 years ago

The identity function works, because each reducer has only one argument? That is, in the first iteration when accumulator is x=>x then ...args is a single value?

Stephen James
Stephen James
~ 3 years ago

I am confused [1,2,3,4].reduce(isNot2Filter(isEvenFilter(doubleMap(pushReducer))), []) // this was [8]

[1,2,3,4].reduce(compose(isNot2Filter, isEvenFilter, doubleMap)(pushReducer), []) // this is [4, 6, 8]

so they are not the same result? Your compose calls the functions in reverse order to the nested example.

compose(f, g, h)(x) is resulting in h(g(f(x))) not f(g(h(x)))

[1,2,3,4].reduce(compose(doubleMap, isEvenFilter, isNot2Filter)(pushReducer), []) // this is [8]

Paul Frend
Paul Frendinstructor
~ 3 years ago

@stephen you're right to be confused as there's a bug in the compose function, sorry about that. accumulation should be calling fn instead of the reverse. I will update this over the weekend, thanks for pointing it out.

Stephen James
Stephen James
~ 3 years ago

Well on the upside I really understand what it is doing now. :)

Dave Garwacke
Dave Garwacke
~ 3 years ago

I was really scratching my head on that one, thanks @stephen!

Paul Frend
Paul Frendinstructor
~ 3 years ago

For anyone reading this thread - the bug in the code has been fixed in the video.

Greg Jones
Greg Jones
~ 3 years ago

Isn't x => x unused in this case? It seems like each function that receives it is just expecting the single argument of reducer, but we're adding x => x as a second argument.

Paul Frend
Paul Frendinstructor
~ 3 years ago

@greg - the identity function (x => x) is purely there to make sure we don't throw an error if you call compose without any arguments, as you can't call .reduce on an empty array without it. If you call compose with nothing but have the identity function there it will still work. I.e. calling compose()() will be undefined.

If you don't mind that it throws an error - you can remove it.

Edit / update: @greg you're spot on. Sorry was looking at my code samples not the code in the video. A bracket is in the wrong spot so the identity fn is not being passed as the second arg to .reduce.

Correct compose fn:

const compose = (...functions) =>
  functions.reduce((accumulation, fn) =>
    (...args) => accumulation(fn(...args)), x => x);
Tobias Barth
Tobias Barth
~ 3 years ago

I watched the last three videos twice because I was confused by the bug. And then I was confused because it was fixed and I didn't know why it was not confusing me anymore. :) Thanks for the update!

Greg Jones
Greg Jones
~ 3 years ago

@Paul, in the video you're not passing x => x as the initial state to the .reduce, it's being passed on the reducer. So the function should still throw an error if invoked with an empty array. Is x => x the right approach when it doesn't match the signature of the input reducers?

Divyendu
Divyendu
~ 3 years ago

Awesome stuff, quick question. What kind of sorcery are you using to show the value inline like a REPL?

Grzegorz Laszczyk
Grzegorz Laszczyk
~ 3 years ago

@Divyendu I belive it is https://quokkajs.com/