Partial application for JavaScript

Let’s write a unit test for DateTime.getDayInYear()

var yearSizes = [366, 365, 365, 365, 366, 365, 365, 365, 366, 365, 365]

expect(_.map(_.range(2000, 2010), function(year) { 
  return DateTime.getDayInYear(year, 11, 31)
})).toEqual(yearSizes)

This looks quite verbose and unreadable, so let’s refactor it to look like this

expect(_.map(_.range(2000, 2010), toLastDateOfYear)).toEqual(yearSizes)

function toLastDateOfYear(year) { return DateTime.getDayInYear(year, 11, 31)}

But this feels silly if we need that function only once.
The solution is to use partial application, which allows any of the arguments to be left undefined by returning a function which takes in the remaining arguments.

expect(_.map(_.range(2000, 2010), DateTime.getDayInYear.papply(__, 11, 31)))
  .toEqual(yearSizes)

Isn’t that beautiful! Here’s some more examples of how partial application can be used


//EXAMPLE
function sum(a, b, c, d) { return a + b + c + d}

//All following examples return 'abcd'
sum.papply('a', 'b', 'c', 'd')
sum.papply('a', 'b')('c', 'd')
sum.papply('a')('b')('c')('d')
var ab = sum.papply('a', 'b')
ab('c','d')
ab('c','d')
ab('c')('d')
sum.papply('a', __, 'c', __)('b','d') 
sum.curry('a', __, 'c', __)('b')('d') 

And finally, here’s the implementation

var __ = {}
function papply(func, givenArguments) {
  var indexOf = givenArguments.indexOf(__)
  var givenArgsSize = givenArguments.length
  var requiredArgsSize = func.length
  if(givenArgsSize >= requiredArgsSize && (indexOf < 0 || indexOf >= requiredArgsSize))
    return func.apply(func, givenArguments)
  else
    return function() {
      return (function(givenArguments, remainingArguments) {
        for(var i = 0; i < givenArguments.length; i++)
          if(givenArguments[i] === __) givenArguments[i] = remainingArguments.shift()
        return papply(func, givenArguments.concat(remainingArguments))
      })(givenArguments.slice(0), Array.prototype.slice.call(arguments))
    }
}

Function.prototype.papply = function() {
  return papply(this, Array.prototype.slice.call(arguments))
}

The code can be found also as Gist in Github

Advertisements

2 comments

    • eeroan

      Thanks for the tip! I didn’t know bind has already native support. However, papply behaves slightly differently: it will always return direct value when given arguments match expected arguments, arguments in the middle can be left unspecified, it behaves recursively (returned function also supports partial application)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s