Namespace Pollution Mechanism

    partitura

    1.0.0 • Public • Published

    Stand-alone implementation of _.partial. With several advantages.


    The Need

    If you are at least a bit familiar with functional programming in Javascript, you know about underscore.js.

    If you are familiar with functional programming a bit more, you've definitely heard all this wails "Underscore is not True functional tool! A True functional approach is 'function-first, data-last' signature and currying! So I'll better use a {library_name} instead".

    If you are not, here is an example.

    It's really strange how can these people claim it "wrong", taking into account that it is specially designed, first of all, to provide an awesome chaining feature support. But ok, lets listen to them.

    There are countless libraries, which "doing it right" (like this one), or which offer some tools to make your function to behave as you want.

    What if I don't want (or, maybe, can not) to switch to a new library, to learn it's API, to check which of habitual to me methods are and aren't present there - but still want to enjoy a "True functional programming"? Well, this is what this plugin for.

    A core idea

    Underscore (and so, Lodash too) provides a partial function, which has great feature - a placeholders. Using an Underscore object itself as placeholder is really great idea - it looks (and behaves!) just like sparse parameters in some "serious" languages.

    So, to achieve a "function-first, data-last" signature we can do this:

    var plus1 = function(value) { return value + 1; };
    var add1 = _.partial(_.map, _, plus1);
    add1([1, 2, 3]) // 2, 3, 4

    Obviously, it's too verbose to be convenient. Mostly, due to explicit using of partial. This is the problem a guys from previous paragraph are talking about. A much more convenient is just to write at least this:

    var add1 = _.map(_, plus1);

    And, actually, this is what this little plugin allows.

    Yes, it's not a "True" auto-currying. It's not a currying at all - it's just a partial application. But I don't think that manually adding a little underscore symbol is so big deal. And we have a significant flexibility and control over parameters order instead (see below).

    So let see how it works.

    How it works

    Plugin provides you an object with one main create method. It creates a helper, providing an implementation of partial function, which will use a given object as a placeholder. If we are already using Underscore and have global _ object, it may look like this:

    var partialize = require('partitura').create(_);
    var myMap = partialize(_.map);
    var add1 = myMap(_, plus1);
    add1([1, 2, 3]); // [2, 3, 4]

    That's it. Simple, isn't it?

    A one big difference

    In difference from from traditional currying approach, Partitura does not check given function's parameters count. You may already know what problems that approach causes - function can have variable parameters count, even 0 positional parameters at all - and then we need to invent something to create a curried function we really need. Here is good article about this.

    Instead of this, our little, but brave helper just checks whether you've passed a placeholder to it. Simple, practical and logical - if you're passing a placeholder, you need a partial application; if you're not - you want to execute source function.

    For example. map accepts a function context as third parameter. Let's modify our plus1 function a bit and check how it works:

    var plus1AndLogContext = function(value) { 
       console.log(this);  
       return value + 1; 
    };
     
    var myMap = partialize(_.map);
    var add1 = myMap(_, plus1AndLogContext);
    add1([1, 2, 3]); // logs Window object 3 times, returns [2, 3, 4]
     
    var add1WithCtx = add1([1, 2, 3], _);
    console.log( typeof add1WithContext  ) // "function"
    add1WithCtx({ x: 42 }) // logs "{ x: 42 }" 3 times, returns [2, 3, 4]

    As parameters count isn't checked, you can go crazy with placeholders:

    add1([1,2,3], _, _)(_)(_, _)(_, _, _)(_)({ x: 42 }) // still returns [2, 3, 4]

    I think there is no need to do something to deny to write such madness - as it still works after all.

    Patching objects

    Well, all this is ok, but remember what we've started from - using a "right library". Patching each function manually has absolutely no difference from origin _.partial(_.map, ...) approach.

    Here an all helper comes. You can pass an object to it, and all it's methods will be turned in "partialized" versions:

    var partialize = require('partitura').create(_);
    partialize.all(_); // patch Underscore itself
    var add1 = _.map(_, plus1);

    Actually, exactly for this plugin was originally designed. But than I got carried away a bit)

    Well, I was not fully honest when told that source function's parameters count isn't checked. all helper processes only functions which have more than 1 parameter. It's done for optimization purposes. For example, Underscore has a lot of single-argument methods, and there is no need to wrap each of them, creating a lot of new functions and consuming additional memory. To handle edge case with functions with variable parameters count, there is a special force option (see below).

    Options

    all helper has some options to configure it's behavior:

    • force: Array - list here methods which should be wrapped disregarding their's parameter count. Use it for functions with variable parameters count.

    Default: []

    • ownOnly: Boolean - whether to process only own properties of given object.

    Default: true

    • saveAs: String | undefined - maybe, you don't want to replace origin methods in object. Don't know, why, but you can. In this case, specify this option, and partialized function will be stored in property of origin one.
    partialize.all(_, { saveAs: 'use' });
    var add1 = _.map.use(_, plus1);

    Default: undefined

    Adapters

    Adapter is pre-created Partitura instance with pre-defined force option - to deal with features of specific library. For now only underscore adapter is defined. Syntax:

    require('partitura').adapters.underscore.patch(_); // need to explicitly pass a lib object

    A saveAs option value can be passed as second parameter to patch.

    Install

    npm i partitura

    DownloadsWeekly Downloads

    2

    Version

    1.0.0

    License

    MIT

    Last publish

    Collaborators

    • jeron-diovis