0

I've kind of grasped some knowledge on functional programming but can't really wrap my head around this function programming block of code. I didn't really knew where I should ask something like this I couldn't figure out so asked it here. So I would really appreciate if someone will help me understand what this higher order function, or a monads example doing?

P.S this code is from composing software book by Eric Elliot

const f = n => n+1;
const g = n => n*2;

This composeM functions is made to compose and map or over numbers of functions? I know reduce but really have no idea how this function should be working.

const composeM = (...mps) => mps.reduce((f, g) => x => g(x).map(f));
const h = composeM(f,g);
h(20)

Then, the function composeM was more generalized by doing:

const compose = methods => (...mps) => mps.reduce((f, g) => x => g(x)[method](f));

Then, I could create composedPromises or composedMaps like

const composePromises = compose("then")(f,g);

How is the g(x)[method](f) even working? it should be g(x).then(f).

Update above map composeM function not working

const f = n => Promise.resolve( n+1 );
const g = n => Promise.resolve( n*2 );

const composePromises = (...mps) => mps.reduce((f, g) => x => g(x).then(f))
const h = composePromises(f, g)
h(20)
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
rakesh shrestha
  • 1,335
  • 19
  • 37
  • 1
    Yes, if `method` is the string `"then"` then the expression `g(x)[method](f)` does exactly the same thing as `g(x).then(f)`. That's just [basic property access](https://stackoverflow.com/questions/4968406/javascript-property-access-dot-notation-vs-brackets). – Bergi Jul 28 '19 at 19:52
  • Is it `composeM` or `composeP`? If you meant the same, then `h(20)` *does not work*, because numbers don't have a `map` method, and we don't have any idea how it maybe was meant to work either. – Bergi Jul 28 '19 at 19:54
  • Ok, thanks for the edit, but that still throws a `Uncaught TypeError: g(...).map is not a function`. – Bergi Jul 28 '19 at 20:00
  • @Bergi yes it's property access.. since promises are objects as well. You've same me the trouble. No, its `h20` its `partial application`. `20` is the third parameter to `composeM` its the `x`. I suppose ` g(x).map(f)` is `x.map(g).map(f)` . he mentions being associative law.. don't know how.. anyway thank you so much – rakesh shrestha Jul 28 '19 at 20:03
  • @Bergi I have updated the example with promise example please check – rakesh shrestha Jul 28 '19 at 20:14
  • 1
    No, `g(x)` is not the same as `x.map(g)`, at least not without being more specific about what `g` and `x` are. (And it certainly doesn't hold if `x` is a number like `20`). – Bergi Jul 28 '19 at 20:21
  • Thanks for the working example, that's something we can work with. But what exactly is your question now? – Bergi Jul 28 '19 at 20:22
  • ha ha oh dear god. I understood it. I was asking how the `composePromises` function was working. It was simple executing from right to left like a normal `compose` functions do.. Thank you @Bergi. You made me realise it. :D. just wanted to ask another thing, that reduce function I have a `.then(f)`. `f` is supposed to be a function but i haven't set any initial value as you normall provide on `reduce` so its `.then(undefined)` ? – rakesh shrestha Jul 28 '19 at 20:29

1 Answers1

1

Consider function composition, which has the following type signature.

// compose :: (b -> c) -- The 1st argument is a function from b to c.
//         -> (a -> b) -- The 2nd argument is a function from a to b.
//         -> (a -> c) -- The final result is a function from a to c.

//               +-----------------b -> c
//               |  +---------a -> b
//               |  |     +-- a
//               |  |     |
const compose = (f, g) => x => f(g(x));
//                             |_____|
//                                |
//                                c

The composeP function is similar to the compose function, except it composes functions that return promises.

// composeP :: (b -> Promise c) -- The 1st argument is a function from b to promise of c.
//          -> (a -> Promise b) -- The 2nd argument is a function from a to promise of b.
//          -> (a -> Promise c) -- The final result is a function from a to promise of c.

//                +-------------------------b -> Promise c
//                |  +-------- a -> Promise b
//                |  |     +-- a
//                |  |     |
const composeP = (f, g) => x => g(x).then(f);
//                              |__________|
//                                   |
//                               Promise c

Remember that the then method applies the callback function to the value of the promise. If we replace .then with [method] where method is the name of the bind function of a specific monad, then we can compose functions that produces values of that monad.

For example, .flatMap is the bind function of arrays. Hence, we can compose functions that return arrays as follows.

// composeA :: (b -> Array c) -- The 1st argument is a function from b to array of c.
//          -> (a -> Array b) -- The 2nd argument is a function from a to array of b.
//          -> (a -> Array c) -- The final result is a function from a to array of c.

const composeA = (f, g) => x => g(x).flatMap(f);

// f :: Bool -> Array String
const f = x => x ? ["yes"] : ["no"];

// g :: Int -> Array String
const g = n => [n <= 0, n >= 0];

// h :: Int -> Array String
const h = composeA(f, g);

console.log(h(-1)); // ["yes", "no"]
console.log(h(0));  // ["yes", "yes"]
console.log(h(1));  // ["no",  "yes"]

That was a very contrived example but it got the point across.

Anyway, the generic compose function composes monads.

const compose = method => (f, g) => x => g(x)[method](f);

const composeP = compose("then");
const composeA = compose("flatMap");

Finally, the various monad compose functions only compose two functions at a time. We can use reduce to compose several of them at once. Hope that helps.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • Oh my lord.. such a beautiful explanation. loved it.. Is there anyway that I can reach you via emails or insta's. Oh by the way can you little bit explain me what a monad is? .. I at a point where I think monad is something which can be flatmapped – rakesh shrestha Aug 01 '19 at 06:41
  • If you have a programming related question then you're better off asking it on StackOverflow instead of asking me directly. In doing so, you're more likely to get an answer because more people will read your question. – Aadit M Shah Aug 01 '19 at 06:47
  • Your intuition about a monad is correct. A monad is any data structure that can be mapped and flattened. For example, consider the following list, `[1,2,3]`. We can map over this list to produce another list. For example, `[1,2,3].map(x => 3 * x).map(x => [x - 2, x - 1, x])` will produce the nested list `[[1,2,3],[4,5,6],[7,8,9]]`. We can then flatten this nested list to produce `[1,2,3,4,5,6,7,8,9]`. – Aadit M Shah Aug 01 '19 at 06:52
  • Another way to think about monads is as *effectual* contexts and holes. Consider the following expression `x + 1`. This expression has a hole named `x`. We can plug this hole with any number we want. For example, when `x = 1` then `x + 1 = 2`. Now, what if we wanted to plug this hole with multiple values? Or what if we wanted to plug this hole with a nullable value? Or what if we wanted to plug this hole with a value that we might get in the future? These are all effectual computations which produce 0, 1, or multiple numbers which can be used to plug the hole. – Aadit M Shah Aug 01 '19 at 07:17
  • The monad bind operation is what allows us to do this. The bind operation has the following type signature, `Monad m => m a -> (a -> m b) -> m b`. Here we have an expression with a hole, `(a -> m b)`. This hole can be plugged with a value of type `a`. We also have an effectful computation `m a` which produces values of type `a`. The bind operation allows us to plug the hole in the expression `(a -> m b)` with values from the effectful computation `m a`, and combine all the resulting effects. Combining all the resulting effects is what you call flattening. – Aadit M Shah Aug 01 '19 at 07:22