Книга: Functional Programming in JavaScript
Назад: Function manipulation
Дальше: Currying

Partial application

Notice that our function factory example's and functions only work for functions that have exactly two arguments. We could write new ones that work for different numbers of arguments, but that would work away from our model of generalization.

What we need is partial application.

Note

Partial application is the process of binding values to one or more arguments of a function that returns a partially-applied function that accepts the remaining, unbound arguments.

Unlike the function and other built-in methods of the object, we'll have to create our own functions for partial application and currying. There are two distinct ways to do this.

  • As a stand-alone function, that is,
  • As a polyfill, that is,

Polyfills are used to augment prototypes with new functions and will allow us to call our new functions as methods of the function that we want to partially apply. Just like this:

Partial application from the left

Here's where JavaScript's and utilities become useful for us. Let's look at a possible polyfill for the Function object:

Function.prototype.partialApply = function(){   var func = this;    args = Array.prototype.slice.call(arguments);   return function(){     return func.apply(this, args.concat(       Array.prototype.slice.call(arguments)     ));   }; };

As you can see, it works by slicing the special variable.

Note

Every function has a special local variable called that is an array-like object of the arguments passed to it. It's technically not an array. Therefore it does not have any of the Array methods such as and . That's why we need to use Array's method to slice the arguments.

And now let's see what happens when we use it in an example. This time, let's get away from the math and go for something a little more useful. We'll create a little application that converts numbers to hexadecimal values.

function nums2hex() {   function componentToHex(component) {     var hex = component.toString(16);     // make sure the return value is 2 digits, i.e. 0c or 12     if (hex.length == 1) {       return "0" + hex;     }     else {       return hex;     }   }   return Array.prototype.map.call(arguments, componentToHex).join(''); }  // the function works on any number of inputs console.log(nums2hex()); // '' console.log(nums2hex(100,200)); // '64c8' console.log(nums2hex(100, 200, 255, 0, 123)); // '64c8ff007b'  // but we can use the partial function to partially apply // arguments, such as the OUI of a mac address var myOUI = 123; var getMacAddress = nums2hex.partialApply(myOUI); console.log(getMacAddress()); // '7b' console.log(getMacAddress(100, 200, 2, 123, 66, 0, 1)); // '7b64c8027b420001'  // or we can convert rgb values of red only to hexadecimal var shadesOfRed = nums2hex.partialApply(255); console.log(shadesOfRed(123, 0));   // 'ff7b00' console.log(shadesOfRed(100, 200)); // 'ff64c8'

This example shows that we can partially apply arguments to a generic function and get a new function in return. This first example is left-to-right, which means that we can only partially apply the first, left-most arguments.

Partial application from the right

In order to apply arguments from the right, we can define another polyfill.

Function.prototype.partialApplyRight = function(){   var func = this;    args = Array.prototype.slice.call(arguments);   return function(){     return func.apply(       this,       [].slice.call(arguments, 0)       .concat(args));   }; };  var shadesOfBlue = nums2hex.partialApplyRight(255); console.log(shadesOfBlue(123, 0));   // '7b00ff' console.log(shadesOfBlue(100, 200)); // '64c8ff'  var someShadesOfGreen = nums2hex.partialApplyRight(255, 0); console.log(shadesOfGreen(123));   // '7bff00' console.log(shadesOfGreen(100));   // '64ff00'

Partial application has allowed us to take a very generic function and extract more specific functions out of it. But the biggest flaw in this method is that the way in which the arguments are passed, as in how many and in what order, can be ambiguous. And ambiguity is never a good thing in programming. There's a better way to do this: currying.

Назад: Function manipulation
Дальше: Currying

bsn
thank
Vesa Karvonen
I hope you don't mind, but I’d like to point you and your readers to my high-performance optics library for JavaScript that is in production use in multiple projects, has comprehensive support for partial optics and interactive documentation: https://calmm-js.github.io/partial.lenses/ (this takes a moment to load — be patient!)