More stuff on Currying and Promises

I know my last post was on currying but this is tool that has confounded me for a long time and I'm finally coming around to understanding what are appropriate use cases for it.

The problem with currying is I understand the concept. I understand what it is doing and what problems it solves. But where does one actually use it in real world code. I've been on the lookout at work and trying to shoehorn it in wherever possible but it hasn't quite fit until recently.

function sendRequest(res, template, context) {  
  if (!res.finished) {
      res.send(template(context)));
  }
}

This was the first chunk of code where the light finally turned on for me.

I love having the params passed in to this function. It makes this function pure and easy to write a unit test against. The problem was that it needed to be used in differents contexts where we only have 1 or 2 of the arguments. THIS is where currying comes in handy. Here is some context. This code is a serverside expressjs app.

Here is the work we do to get out index.html from the filesystem:

fs.readFile('index.html', 'utf-8', (err, file) => {  
  var template = _.template(indexHtml);
  // template is available
});

Here is a service call we make to fetch articles for our page:

articleService.getLatestArticles()  
  .then((context) => {
    //context is available
  }).catch(e => {console.error(e)})

Here is our express route:

app.get('/articles', (req, res) => {  
  //req is available
});

All of this might be overwhelming and ... it is. This is alot of context to juggle. Luckily we have a couple tricks to clean it up. First lets make a Promise for the filesystem read

function getIndex() {  
  return new Promise(resolve, reject){
    fs.readFile('index.html', 'utf-8', (err, file) => {
        if (err) {reject(err)}
          resolve(file);
    });
  }
}

Now lets use Promise#all so we fire off our calls at the same time but and resolve once both are complete.

var serviceCalls = [  
  articleService.getLatestArticles(),
  getIndex()
];

Promise.all(serviceCalls)  
  .then(([context, index]) => {
      // Now we have both our context and index.
  }).catch(e => {console.error(e)})

It's important to understand that these service calls are fetching their data at the same time and we only resolve once both have been completed. The following code executes one call after another which is much slower:

  articleService.getLatestArticles()
      .then(getIndex)
    .then(([context, index]) => {
        // context is `undefined` unless you plumb it through.
        // This is a sloppy pattern. Try to avoid using it and use Promise#all wherever you can.
    });

Anyways... So now we have our data and we need to send it back to the client. Let's put it all together:

function sendRequest(res, template, context) {  
  if (!res.finished) {
      res.send(template(context)));
  }
}

app.get('/articles', (req, res) => {  
  var serviceCalls = [
    articleService.getLatestArticles,
    getIndex
  ];

  Promise.all(serviceCalls)
    .then(([context, index]) => {
      sendRequest(res, _.template(index), context);
    }).catch(e => {console.error(e)})

});

This code works fine, so where does currying come in? It doesn't really solve any problems in this code but now that we understand the above problem lets play with currying a bit.

function sendRequestNotCurried(res, template, context) {  
  if (!res.finished) {
      res.send(template(context)));
  }
}

var sendRequest = _.curry(sendRequestNotCurried);

app.get('/articles', (req, res) => {  
  // Now that we have our req. Let's make a helper so we don't have to worry about it anymore.
  var wantsTemplateAndContext = sendRequest(req);
  var serviceCalls = [
    articleService.getLatestArticles,
    getIndex
  ];

  // Promise.all rejects all promises if one fails. We are using Q#allSettled so we can still send back an imcomplete page to the client without sending a 500 error.
  Q.allSettled(serviceCalls)
    .then(([contextResult, indexResult]) => {
        var index = indexResult.value;
        // Ignoring error handling for if the index.html fetch fails, let's create a function that can send a response to the client that takes different contexts.
        var wantsContext = wantsTemplateAndContext(index);
        var context;
        if (contextResult.state === 'fulfilled') {
          context = contextResult.value;
        } else {
          context = {errorMessage: 'Oops!'};
        }
        //Now we send out response back to the client. All arguments are satisfied now and `sendRequestNotCurried` will be called.
        wantsContext(context);
    });

});

This wasn't the greatest example of a good spot to use currying. The reason why I used it though is that in a real application, passing a function around to different parts of the program that just want a single argument is very useful. Currying is great for making functions that take many arguments easier to reason about. Also it makes the functions easier to test.