dimitris papadimitriou
dimitris papadimitriou More than 12 years’ experience as full stack developer and Software Architect . Functional javascript with categories.

Promise as Functor (Video)

Promise as Functor (Video)

Idea: The Promise then can be seen as a map method that applies a function to the expected value.

Async Id Functor

this is more of an idea i came up in order to make more obvious that a Promise is just an asynchronous identity functor ( or an async Either functor if you include an error callback) .

We already talk about how the Id entity functor is the minimal functor in a js world

1
2
3
4
const Id = (v) => ({
  value: v,
  map: (f) => Id(f(v))
});

It doesn’t matter if we don’t have the value yet since we can instead of the v leave a subscription callback that will eventually have the value so we can write that in this manner

1
2
3
4
const Id= (executor) => ({
         map: (f) =>
});

Which we will use in this way when we have a value

1
Id(r => r(5)) 

Now in order to create the map we still follow the same steps that we follow to create a map for a Functor

  1. Open the container and access the value v. We can access the value by adding a subscription to the callback like this executor(v=>…) now we have the value (whenever this value becomes available, we do not care .What matters to us is that we have a variable v to treat as a value)

  2. Apply the function f executor(v=>f(v))

  3. We must repackage this into something that is the same as the initial package

    1
    
    Id = (executor) => ({}).
    

    Fortunately for us this form of Id expects a callback and not a value, so we have to pass on the constructor something that looks like this

    1
    
    map: (f) => Id(resolve =>  ) 
    

    this means that we can pass the value f(v) from executor (v=>f(v)) into the r and in this way we have completed the construction

What we get is the following:

1
2
3
4
5
6
 var Id = (executor) => ({
  map: f => Id(resolve => executor(v => resolve(f(v)))),
  matchWith: pattern => executor(v => pattern(v))
 })

 Id(resolve => resolve(5)).map(x => x + 4).matchWith(console.log)

Run This: Js Fiddle

Promise as Functor

We will start by extending the Custom Promise seen previously to a functor, by providing a map method that obeys the functor laws and thus promote native Promise into a functor.

The Native implementation of promises (based on the Promises/A+ specification ) has a map achieved by overloading the then:

1
2
3
var thenableMapResult = new Promise((resolve,reject)=>resolve(5))
.then(x=>x+3)
.then(console.log)//8

Run This: Js Fiddle

This Section is clearly to just provide some understanding on the mechanics behind the .then as a map .

We have said that the usual metaphor for a functor is “a container.” A Promise can be seen as a container that takes a value and wraps it, until it is resolved. In order to promote Promise to Functor, there must be a mapping function that would be able to lift any function and give a new Promise with the lifted value.

Here is one possible mapping function that preserves structure:

1
2
3
4
5
6
7
    Promise.prototype.map = function(f) {  
    var initialPromise = this;    
       return new Promise(function(resolve,reject) {    
          initialPromise.then(result => resolve(f(result)))        
           .catch(reject);   
        });  
    } 

Run This: Js Fiddle

The mapping function that lifts the function f: a→b follows the steps:

  1. Waits for the result of the Promise (so in a way unwraps the contained value) initialPromise.then( )
  2. Applying the function f : result => resolve(f(result))
  3. wraps the resulting value again into a new Promise : return new Promise() because when we implement a map, we always return something of the same type in order to belong to the same category (in this case type) or Promise.

Something worth pointing out here is the .catch(reject) this part belongs to the path of failure and ignores the f mapping. We are going to come back to this observation many times throughout this book.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var fetchClientsMock = () => new Promise((resolve, reject) => {
  setTimeout(() => {
   resolve(
  [{ id: 1, name: 'jim', age: 29 },
  { id: 2, name: 'jane', age: 25 }])
  }, 1000);
 })

 var clientRepository = ({
  getById: (id) =>
  fetchClientsMock()
  .map(clients => 
      clients.filter(c => c.id == id))//promise of a filtered array matching id
 });

 
 var clientNameById=id => clientRepository
 .getById(id)
 .map(x => x[0].name);//might throw exception if array is empty   
  clientNameById(1)
  .then(x => console.log(`the client name is ${x}`))

 

Run This: Js Fiddle

comments powered by Disqus