• No results found

Partial application and parameter binding

Toward modular, reusable code

4.4 Partial application and parameter binding

const appender = appenders[appender];

appender.setLayout(layouts[layout]);

const logger = new Log4js.getLogger(name);

logger.addAppender(appender);

logger.log(level, message, null);

};

Now, by currying logger, you can centrally manage and reuse appropriate loggers for each occasion:

const log = R.curry(logger)('alert', 'json', 'FJS');

log('ERROR', 'Error condition detected!!');

// -> this will popup an alert dialog with the requested message

If you’re implementing multiple error-handling statements into one function or file, you also have the flexibility of partially setting all but the last parameter:

const logError = R.curry(logger)('console', 'basic', 'FJS', 'ERROR');

logError('Error code 404 detected!!');

logError('Error code 402 detected!!');

Behind the scenes, subsequent calls to curry are called on this function, finally yield-ing a unary function. The fact that you’re able to create new functions from existyield-ing ones and pass any number of parameters to them leads to easily building functions in steps as arguments are defined.

In addition to gaining lots of reusability in your code, as I mentioned, the principal motivation behind currying is to convert multiargument functions into unary func-tions. Alternatives to currying are partial function application and parameter binding, which are moderately supported by the JavaScript language, to produce functions of smaller arity that also work well when plugged into function pipelines.

4.4 Partial application and parameter binding

Partial application is an operation that initializes a subset of a nonvariadic function’s parameters to fixed values, creating a function of smaller arity. In simpler terms, if you have a function with five parameters, and you supply three of the arguments, you end up with a function that expects the last two.

Like currying, partial application can be used to directly reduce the length of a func-tion, but in a slightly different manner. Because a curried function is, essentially, a par-tially applied function, there tends to be confusion about the techniques. Their main

Defines a set of canned layout providers

Issues a logging statement with all configuration parameters applied

Evaluates all but the last two arguments

difference lies in the internal mechanism and control over parameter passing. I’ll attempt to clarify:

Currying generates nested unary functions at each partial invocation. Inter-nally, the final result is generated from the step-wise composition of these unary functions. Also, variations of curry allow you to partially evaluate a number of arguments; therefore, it gives you complete control over when and how evalua-tion takes place.

Partial application binds (assigns) a function’s arguments to predefined values and generates a new function of fewer arguments. The resulting function con-tains the fixed parameters in its closure and is completely evaluated on the subse-quent call.

Now that this is clear, let’s move on to examine a possible implementation of partial.

function partial() {

let fn = this, boundArgs = Array.prototype.slice.call(arguments);

let placeholder = <<partialPlaceholderObj>>;

let bound = function() {

let position = 0, length = args.length;

let args = Array(length);

for (let i = 0; i < length; i++) { args[i] = boundArgs[i] === placeholder

? arguments[position++] : boundArgs[i];

}

while (position < arguments.length) { args.push(arguments[position++]);

For this discussion of partial application and function binding, we’ll go back to using Lodash, because it has slightly better support for function binding than Ramda. On the surface, however, using _.partial has a similar feel to using R.curry, and both support placeholder arguments with their respective placeholder objects. With the same logger function shown earlier, you can partially apply certain parameters to cre-ate more-specific behavior:

const consoleLog = _.partial(logger, 'console', 'json', 'FJS Partial');

Let’s use this function to reemphasize the difference between curry and partial.

After applying these three arguments, the resulting consoleLog function expects the Listing 4.7 Implementation of partial

Implementations of partial in libraries such as Lodash use the underscore object as the placeholder. Other ad hoc implementations use undefined to suggest this parameter should be skipped. defining a function’s parameter for a later call, so you can pick which parameters are bound and which are supplied as part of the call (examples shortly).

Uses

other two arguments when called (not in steps, but all at once). So, unlike currying, calling consoleLog with just one argument won’t return a new function and will instead evaluate with the last one set to undefined. But you can continue applying partial arguments to consoleLog by using _.partial again:

const consoleInfoLog = _.partial(consoleLog, 'INFO');

consoleInfoLog('INFO logger configured with partial');

Currying is an automated way of using partial applications—this is its main difference from partial. Another variation is function binding, which is also available natively in JavaScript as Function.prototype.bind().1 It works a bit differently than partial does:

const log =_.bind(logger, undefined, 'console', 'json', 'FJS Binding');

log('WARN', 'FP is too awesome!');

What is this undefined second argument to _.bind? Bind lets you create bound func-tions, which can execute within the context of an owning object (passing undefined tells the runtime to bind this function to the global context). Let’s see some practical uses of _.partial and _.bind that do the following:

Extend the core language

Bind delayed functions 4.4.1 Extending the core language

Partial application can be used to extend core data types like String and Number with useful utilities than enhance the expressiveness of the language. Just be mindful that extending the language this way may make your code less portable to platform upgrades if new, conflicting methods are added to the language. Consider the follow-ing examples:

// Take the first N characters

String.prototype.first = _.partial(String.prototype.substring, 0, _);

'Functional Programming'.first(3); // -> 'Fun' // Convert any name into a Last, First format String.prototype.asName =

_.partial(String.prototype.replace, /(\w+)\s(\w+)/, '$2, $1');

'Alonzo Church'.asName(); //-> 'Church, Alonzo'

1 See “Function.prototype.bind(),” Mozilla Developer Network, http://mng.bz/MY75.

Using a placeholder, you can partially apply substring starting at index zero and create a function that expects an offset value.

Partially applies certain parameters to create specific behavior

// Converts a string into an array String.prototype.explode =

_.partial(String.prototype.match, /[\w]/gi);

'ABC'.explode(); //-> ['A', 'B', 'C']

// Parses a simple URL

String.prototype.parseUrl = _.partial(String.prototype.match, /(http[s]?|ftp):\/\/([^:\/\s]+)\.([^:\/\s]{2,5})/);

'http://example.com'.parseUrl(); // -> ['http', 'example', 'com']

Before implementing your own function, make sure to feature-check it first so you can stay on top of new language updates:

if(!String.prototype.explode) {

String.prototype.explode = _.partial(String.prototype.match, /[\w]/gi);

}

There are cases where partial application doesn’t work, such as when you’re working with delayed functions like setTimeout. For this, you need to use function binding.

4.4.2 Binding into delayed functions

Using function binding to set the context object is important when you’re working with methods that expect a certain owning object to be present. For instance, func-tions such as setTimeout and setInterval in the browser expect the this reference to be set to global context, the window object; otherwise, they don’t work. Passing undefined tells the runtime to do just this. For instance, setTimeout can be used to create a simple scheduler object to run delayed tasks. Here’s an example of using both _.bind and _.partial:

const Scheduler = (function () {

const delayedFn = _.bind(setTimeout, undefined, _, _);

return {

delay5: _.partial(delayedFn, _, 5000), delay10: _.partial(delayedFn, _, 10000), delay: _.partial(delayedFn, _, _) };

})();

Scheduler.delay5(function () {

consoleLog('Executing After 5 seconds!') });

Using Scheduler, you can invoke any piece of code wrapped in a function body with a certain delay (this timer isn’t guaranteed by the runtime engine, but that’s a separate issue). Because both bind and partial are functions returning other functions, you can easily nest them. As you can see in the previous code, you build each delay operation Partially applies match with specific regex expressions to transform a string into an array containing specific data

from the composition of a bound function and a partially applied function. Function binding isn’t as useful as partial application in functional programming, and it’s also a bit trickier to use, because it involves once again setting the function context. I cover it here in case you run into it when exploring this topic on your own.

Both partial application and currying are useful. Currying is the most widely used technique to create function wrappers that abstract a function’s behavior, either to preset its arguments or to partially evaluate them. This is beneficial because pure functions with fewer arguments are easier to work with than functions with many argu-ments. Either approach facilitates supplying the proper arguments so that functions don’t have to blatantly access objects outside of their scope, while reducing them to unary functions. Isolating the logic of obtaining this necessary data makes functions more reusable; and, more important, it simplifies their composition.