DEV Community

dimitrispapadimitriou
dimitrispapadimitriou

Posted on • Edited on

Composing Functors

Functors are closed under composition. This means that we should be able to construct a well-behaved map method for the composition.

Functors are closed under composition. The term closed means that if we have two functors and we compose them (have a functor-in-a-functor situation like Maybe<Id<>> or Maybe<Array<>>, Array<Array<>> etc), the resulting structure is also a functor.

This means that we should be able to construct a well-behaved map method for the composition, using only the map methods of the composing functors and nothing more.

Let us examine some specific cases. We will start with the Id<Array<>> case. This is not a Type that you will see in practice but will make the mechanics easier to examine. It’s easy to get a Id<Array<>>. For example we can place some Clients in an Id<>:

Id([new Client(1, "jim", 2), new Client(2, "jane", 3)]) 
Enter fullscreen mode Exit fullscreen mode

now we can define a map for this composition easily

var mapT = composite => f => composite.map(inner => inner.map(f))
Enter fullscreen mode Exit fullscreen mode
import { Client } from "./model.js"
import { Id } from "./Id.js"

let idClients =Id([new Client(1, "jim", 2), new Client(2, "jane", 3)]) ;

let mapT = composite => f => composite.map(inner => inner.map(f))

let idNames = mapT(idClients)(client=>client.name)

 console.log(idNames)  //Id (['jim', 'jane' ] ) 
                       //the result is an Id<Array[String]>
                       // -An Id of an Array of Strings


Enter fullscreen mode Exit fullscreen mode

Run This

You will use the map of each Functors to pass the function client=>client.name one level deeper.
It’s easy to see that this pattern .map(_->_.map(_->_)) will appear in any combination of functors. Look at another combination just to confirm this. For an Array<Array<>>

import { Client } from "./model.js"


let groupsOfClientsByDepartment = [
    [new Client(1, "jim", 2), new Client(2, "jane", 3)],
    [new Client(3, "kate", 2), new Client(4, "John", 3)]];

var mapT = composite => f => composite.map(inner => inner.map(f))

var groupsOfClientNamesByDepartment = mapT(groupsOfClientsByDepartment)(client => client.name)

console.log(groupsOfClientNamesByDepartment)  //[ [ 'jim', 'jane' ], [ 'kate', 'John' ] ]
                       //the result is an Array<Array[String]>
                       // -An Array of an Array of Strings 
Enter fullscreen mode Exit fullscreen mode

Run This

It is the same .map(_->_.map(_->_)) pattern again.

Transformer Types

Another way to express this composition is by creating an adapter that we usually refer to as “transformers” that would expose a single .map(_)

export const ComposeT = composition => ({
  map: f => ComposeT(composition.map(inner => inner.map(f))),
  unwrap: () => composition
});
Enter fullscreen mode Exit fullscreen mode

we can use this IdArrayT transformer in the following way :

Run This

The main computation now becomes :

let idNames = ComposeT(idClients)
                 .map(client => client.name)
                 .map(name => name.toUpperCase())
                 .unwrap();
Enter fullscreen mode Exit fullscreen mode

*In the next sections we are going to use those ideas to deal with the Promise<Maybe<>> and Promise<Either<>> combinations.

Excerpt from the free course https://www.fluidinfunctional.com/jscourse/ which is a self-standing subset of the book “Functional Programming in Javascript”
Alt Text
https://www.fluidinfunctional.com/jscourse/

Top comments (0)