7

I am reading a book which contains the following example:

var composition1 = function(f, g) {
  return function(x) {
    return f(g(x));
  }
};

Then the author writes: "...naive implementation of composition, because it does not take the execution context into account..."

So the preferred function is that one:

var composition2 = function(f, g) {
  return function() {
    return f.call(this, g.apply(this, arguments));
  }
}; 

Followed by an entire example:

var composition2 = function composition2(f, g) {
    return function() {
        return f.call(this, g.apply(this, arguments));
    }
};

var addFour = function addFour(x) {
    return x + 4;
};

var timesSeven = function timesSeven(x) {
    return x * 7;
};

var addFourtimesSeven2 = composition2(timesSeven, addFour);
var result2 = addFourtimesSeven2(2);
console.log(result2);

Could someone please explain to me why the composition2 function is the preferred one (maybe with an example)?

EDIT:

In the meantime i have tried to use methods as arguments as suggested, but it did not work. The result was NaN:

var composition1 = function composition1(f, g) {
    return function(x) {
        return f(g(x));
    };
};

var composition2 = function composition2(f, g) {
    return function() {
        return f.call(this, g.apply(this, arguments));
    }
};

var addFour = {
    myMethod: function addFour(x) {
        return x + this.number;
    },
    number: 4
};

var timesSeven = {
    myMethod: function timesSeven(x) {
        return x * this.number;
    },
    number: 7
};

var addFourtimesSeven1 = composition1(timesSeven.myMethod, addFour.myMethod);
var result1 = addFourtimesSeven1(2);
console.log(result1);

var addFourtimesSeven2 = composition2(timesSeven.myMethod, addFour.myMethod);
var result2 = addFourtimesSeven2(2);
console.log(result2);
4
  • This actually has nothing to do with currying. Commented Apr 16, 2016 at 10:01
  • You are right, I changed the wording. Commented Apr 16, 2016 at 13:16
  • It is called FUNCTION composition! Why do you compose methods for heaven's sake? Function composition means to combine pure functions in curried form without side effects in point-free style: const compose = f => g => x => f(g(x)). Curried functions except only a single argument (as our compose function). Function composition only works with curried functions, because a function only returns a single value. Commented Jul 24, 2016 at 21:46
  • Then the author writes: "...naive implementation of composition, because it does not take the execution context into account..." I consider the author as naive. Commented Jul 24, 2016 at 21:55

3 Answers 3

2

This just answers what composition2 actually does:

composition2 is used when you want to keep this as context in the functions itself. The following example shows that the result is 60 by using data.a and data.b:

'use strict';

var multiply = function(value) {
    return value * this.a;
}
var add = function(value) {
    return value + this.b;
}

var data = {
    a: 10,
    b: 4,
    func: composition2(multiply, add)
};

var result = data.func(2);
// uses 'data' as 'this' inside the 'add' and 'multiply' functions
// (2 + 4) * 10 = 60

But yet, it still breaks the following example (unfortunately):

'use strict';

function Foo() {
    this.a = 10;
    this.b = 4;
}
Foo.prototype.multiply = function(value) {
    return value * this.a;
};
Foo.prototype.add = function(value) {
    return value + this.b;
};


var foo = new Foo();

var func = composition2(foo.multiply, foo.add);
var result = func(2); // Uncaught TypeError: Cannot read property 'b' of undefined

Because the context of composition2 (this) is undefined (and is not called in any other way, such as .apply, .call or obj.func()), you'd end up with this being undefined in the functions as well.

On the other hand, we can give it another context by using the following code:

'use strict';
var foo = new Foo();

var data = {
    a: 20,
    b: 8,
    func: composition2(foo.multiply, foo.add)
}

var result = data.func(2); 
// uses 'data' as 'this'
// (2 + 8) * 10 = 200 :)

Or by explicitly setting the context:

'use strict';

var multiply = function(value) {
    return value * this.a;
};
var add = function(value) {
    return value + this.b;
};


var a = 20;
var b = 8;

var func = composition2(multiply, add);

// All the same
var result1 = this.func(2);
var result2 = func.call(this, 2);
var result3 = func.apply(this, [2]);
Sign up to request clarification or add additional context in comments.

5 Comments

I hope this makes sense, if it doesn't let me know. I'll try to rephrase if needed.
Well you can do that, but then you work actively against the language you are using. Take a look at my answer, why it makes no sense to do things like this; although you can do them.
I'll just leave this answer as a reference what the composition2 actually does, but I agree with you. :)
@ Caramiriel: I understood your answer it in the following way and hope this is right :) Your first example: composition2 is called via an object, therefore this = object (in this case 'data') Your second example: composition2 is directly called, therefore this = window object (in this case 'undefined' because of strict mode) Your third example: composition2 is called again via an object, therefore this = object (in this case 'data') Your fourth example: composition2 is directly called, therefore this = window object, and as 'a' and 'b' are properties of the window object it works
@JShinigami Exactly :)
0

composition1 would not pass arguments other than the first to g()

If you do:

var composition1 = function(f, g) {
  return function(x1, x2, x3) {
    return f(g(x1, x2, x3));
  }
};

the function will work for the first three arguments. If you however want it to work for an arbitrary number, you need to use Function.prototype.apply.

f.call(...) is used to set this as shown in Caramiriel's answer.

Comments

0

I disagree with the author.

Think of the use-case for function-composition. Most of the time I utilize function-composition for transformer-functions (pure functions; argument(s) in, result out and this is irrelevant).

2nd. Utilizing arguments the way he does it leads into a bad practice/dead end, because it implies that the function g() might depend on multiple arguments.

That means, that the composition I create is not composable anymore, because it might not get all arguments it needs.
composition that prevents composition; fail

(And as a side-effect: passing the arguments-object to any other function is a performance no-go, because the JS-engine can't optimize this anymore)

Take a look at the topic of partial application, usually misreferenced as currying in JS, wich is basically: unless all arguments are passed, the function returns another function that takes the remaining args; until I have all my arguments I need to process them.

Then you should rethink the way you implement argument-order, because this works best when you define them as configs-first, data-last.
Example:

//a transformer: value in, lowercased string out
var toLowerCase = function(str){
    return String(str).toLowerCase();
}

//the original function expects 3 arguments, 
//two configs and the data to process.
var replace = curry(function(needle, heystack, str){
    return String(str).replace(needle, heystack);
});

//now I pass a partially applied function to map() that only 
//needs the data to process; this is really composable
arr.map( replace(/\s[A-Z]/g, toLowerCase) );

//or I create another utility by only applying the first argument
var replaceWhitespaceWith = replace(/\s+/g);
//and pass the remaining configs later
arr.map( replaceWhitespaceWith("-") );

A slightly different approach is to create functions that are, by design, not intended to get all arguments passed in one step, but one by one (or in meaningful groups)

var prepend = a => b => String(a) + String(b);  //one by one
var substr = (from, to) => value => String(str).substr(from, to);  //or grouped

arr.map( compose( prepend("foo"), substr(0, 5) ) );
arr.map( compose( prepend("bar"), substr(5) ) );
//and the `to`-argument is undefined; by intent

I don't intend to ever call such functions with all the arguments, all I want to pass them is their configs, and to get a function that does the job on the passed data/value.

Instead of substr(0, 5, someString), I would always write someString.substr(0, 5), so why take any efforts to make the last argument (data) applyable in the first call?

3 Comments

mathematically composition is perfectly defined among functions with different number of arguments (or dimension of the domain), as long as the codomain of the first (namely g) matches with the domain of the seond (f) . This is the case for the authors definition, and whilie it is still not the most general definition (which would probably involve forcing all returns to be arrays) it is more general then the "one in -- one out" and also covers your use case.
Furthermore "this" can be seen as an additional argument which has been partially applied to both before applying composition. Of course one could do this by explicitly using "bind" each time, but if there is no "this", there is no harm, but if there is one, it is again very reasonable to take this approach. And there are use cases for it.
My last comment is a question: Do you have a reference or more details behind your statement "passing the arguments-object to any other function is a performance no-go". I am aware that manipulating arguments directly can be problematic, but even if one first turns it into a real argument via the common args = [].slice.call(arguments, 1) , one is already doing exactly your no-go, "passing it to any other function".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.