The True Use of Monads in FP

Part 2: The Motivating Problem in JS

Usher Klein
Descript Software
5 min readJun 5, 2018

--

The Motivating Problem

Your manager asks you to write a function countCharsOfFriendsPosts that, given a user ID, counts the number of characters in posts made by friends of the user. Your manager tells you the user database will be represented as an array in your local memory as displayed below:

Being the expert functional programmer that you are, you quickly recognize countCharsOfFriendsPosts as being the composition of the 3 following smaller functions:

  1. getUserFriends
    Given a user ID, query a database for the user’s friends.
  2. getUsersPosts
    Given an array of users, query the database for posts made by any of the users.
  3. countChars
    Given an array of posts, count the number of characters in the posts.

You then implement the functions as follows:

And finally you have countCharsOfFriendsPosts:

“Wow! What simple elegance,” you think to yourself, “what an amazing specimen of functional programming.” You expect your manager to be thrilled with your extreme powers of composition, however you find him less enthused than expected. He informs you that he made a mistake in the specs and the database that stores a user’s friends is on a remote server and thus needs to be requested asynchronously.

Your current code can be viewed and tested here: Ramda-Repl-1

A (Not-So) Quick Detour On The True Use Of Functors In FP

Your manager quickly convinces you that this was the only mistake he made in the specs, and that the user’s posts are indeed represented in local memory. Undeterred, you rewrite getUserFriends to return a Promise of a List of UserIDs.

However you quickly see a road block ahead; this completely messes up your elegant composition! getUserFriendsAsync returns a Promise (List UserID) while getUserPosts takes a List UserID. The types don’t line up!

Sub-detour 1: Promises And Then

Before we solve this problem I want to clear up some confusion around the JS Promise’s then method. The function passed to the then method has two acceptable signatures:

To avoid possible confusion, instead of using the then method, I will refer to the following two functions:

Sub-detour 2: The Quick and Dirty Definition of A Functor

In a future post I’ll elaborate on the full definition, but for now this is perfect:

A functor F is a function on functions such that for all pairs of functions h and g:

1. If h can be composed after g, then F(h) can be composed after F(g);

2. F(h) ∘ F(g) = F(h ∘ g).

In code: F(c(h, g)) == c(F(h), F(g))

In other words, it doesn’t matter if we first compose h ∘ g and then apply F, or apply F to each and then compose. That’s it. That’s the whole thing.

Back to our story…

Image of Composed Functions

After a brief moment you realize there is only one thing to do here: somehow transform the composition of getUsersPosts and countChars and compose that with getUserFriendsAsync to get a new countCharsOfFriendsPostsAsync as follows:

Here’s a link: Ramda-Repl-2

Although not quite as elegant as your original simple composition, you are quite satisfied with this solution. You are about to get up and pass this off to your manager when you instead see him running up to you. Huffing and puffing, he explains that now that your function is asynchronous, Upper Management insists on providing a Daily Default Post (DDP) to count, should anything go wrong while trying to get the user’s friends’ posts. The DDP will be available in local memory, and your program should count the characters of the DDP if, unfortunately, something goes wrong with getting the user’s friends’ posts.

You quickly implement a catch function with the hope of inserting it where required to provide the DDP should an error have occurred in either getting the user’s friends or the friends’ posts.

You study your current configuration —

pmap(countChars ∘ getUsersPosts) ∘ getUserFriendsAsync

— and realize that you would need to put a “catch” after getUsersPosts, but before countChars. You start sweating when you realize that once again the types don’t line up! pcatch takes and returns a promise of an array of Strings, but the return type of getUsersPosts and the input type of countChars is just an array of Strings!

You loudly lament how if, instead of

pmap(countChars ∘ getUsersPosts),

you had

pmap(countChars) ∘ pmap(getUsersPosts),

all your problems would be solved as then you could compose as follows:

The guy sitting next to you, a functional programmer, whispers in your ear “Promise is a functor” (or if he was a mathematician maybe “pmap is a functor”). You instantly light up. For pmap to be a functor, that would mean that for any functions g and f:

pmap(f ∘ g) = pmap(f) ∘ pmap(g)

And indeed, you can safely replace pmap(countChars ∘ getUsersPosts) with pmap(countChars) ∘ pmap(getUsersPosts).

Thanks to functors, you are confident in the following code:

In short, functors map functions in such a way that composing after mapping or mapping after composing provide the same result.

View it in here: Ramda-Repl-3

Up Next: The Motivating Problem Cont.

Asher Klein is the Founder & CEO of Descript Software. He holds a degree in Pure & Applied Mathematics from Concordia University.

--

--