A Maybe is actually a particular instance of something called a monad. I won’t get into the exact properties of a monad, but for our purposes it is something that has a flatMap, to combine them and change the value, and a pure, to create a new one. In the case of Maybe the pure is Maybe.Some(x). From those two functions you can also always create map as well since it is just map(f) = flatMap(pure(f)). So we can also assume that every monad has a map function.

(Note that flatMap is sometimes known as bind, and pure is sometimes known as unit or identity.)

Some examples of other monads include Result, which contains either the value or an error; List, and by extension array if you have access to flatten with which you can define a flatMap using map; and actually Promise as well if you were to rename resolve to pure and then to flatMap.

This means that if we know the pure function we can generalise our mFor to allow us to use it for any monad. The pure function usually exists as a static method on the class so I find it more readable to pass in the class rather than the pure function directly, but if you prefer to pass in the function then hopefully it is obvious how to change it.

I also assume in the type that our monads are all children of a Monad class. This works because we have defined our monads as such and so we’ve constrained the type this way to denote that we need all the monads to be the same kind of monad, and we expect the same kind out at the end. You can always just define them as any object that has the properties we listed above.

We do need the monads to be the same type because it is not possible to use flatMap to combine two different types of monad.

So how about a real world example of how to use this mFor? Here is an example we use when we have a route on a map and we need to extrapolate to the rest of the route. Our map component (which will feature in a future blog post) expects a list of the origin and destination of each route.

Hopefully you can see how this neatly handles protecting against undefined values, that can come from a range of different sources, with the Maybes and uses the mFor to make it significantly more readable than it would otherwise be.

If you were to add those renamed methods to Promise as I mentioned above you could do a similar thing for multiple asynchronous API calls as part of a load function. The difference between the mFor and Promise.all in this case is that mFor executes the Promises one after the other and can use the results of one in the next. It can be combined with Promise.all where appropriate.

Both of these functions do the same thing but the second version will execute both loadCollections and loadCustomer simultaneously, while the first does them one at a time. This can only be done if they don’t depend on each other so it still has to wait for loadBooking to finish since the subsequent calls use the result.