admin管理员组文章数量:1417070
I am reading a book which contains the following example:
var position1 = function(f, g) {
return function(x) {
return f(g(x));
}
};
Then the author writes: "...naive implementation of position, because it does not take the execution context into account..."
So the preferred function is that one:
var position2 = function(f, g) {
return function() {
return f.call(this, g.apply(this, arguments));
}
};
Followed by an entire example:
var position2 = function position2(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 = position2(timesSeven, addFour);
var result2 = addFourtimesSeven2(2);
console.log(result2);
Could someone please explain to me why the position2 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 position1 = function position1(f, g) {
return function(x) {
return f(g(x));
};
};
var position2 = function position2(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 = position1(timesSeven.myMethod, addFour.myMethod);
var result1 = addFourtimesSeven1(2);
console.log(result1);
var addFourtimesSeven2 = position2(timesSeven.myMethod, addFour.myMethod);
var result2 = addFourtimesSeven2(2);
console.log(result2);
I am reading a book which contains the following example:
var position1 = function(f, g) {
return function(x) {
return f(g(x));
}
};
Then the author writes: "...naive implementation of position, because it does not take the execution context into account..."
So the preferred function is that one:
var position2 = function(f, g) {
return function() {
return f.call(this, g.apply(this, arguments));
}
};
Followed by an entire example:
var position2 = function position2(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 = position2(timesSeven, addFour);
var result2 = addFourtimesSeven2(2);
console.log(result2);
Could someone please explain to me why the position2 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 position1 = function position1(f, g) {
return function(x) {
return f(g(x));
};
};
var position2 = function position2(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 = position1(timesSeven.myMethod, addFour.myMethod);
var result1 = addFourtimesSeven1(2);
console.log(result1);
var addFourtimesSeven2 = position2(timesSeven.myMethod, addFour.myMethod);
var result2 = addFourtimesSeven2(2);
console.log(result2);
Share
Improve this question
edited Apr 16, 2016 at 13:16
JShinigami
asked Apr 16, 2016 at 9:25
JShinigamiJShinigami
8,0753 gold badges18 silver badges22 bronze badges
4
- This actually has nothing to do with currying. – Bergi Commented Apr 16, 2016 at 10:01
- You are right, I changed the wording. – JShinigami Commented Apr 16, 2016 at 13:16
-
It is called FUNCTION position! Why do you pose methods for heaven's sake? Function position means to bine pure functions in curried form without side effects in point-free style:
const pose = f => g => x => f(g(x))
. Curried functions except only a single argument (as ourpose
function). Function position only works with curried functions, because a function only returns a single value. – user6445533 Commented Jul 24, 2016 at 21:46 - Then the author writes: "...naive implementation of position, because it does not take the execution context into account..." I consider the author as naive. – user6445533 Commented Jul 24, 2016 at 21:55
3 Answers
Reset to default 2This just answers what position2
actually does:
position2
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: position2(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 = position2(foo.multiply, foo.add);
var result = func(2); // Uncaught TypeError: Cannot read property 'b' of undefined
Because the context of position2
(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: position2(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 = position2(multiply, add);
// All the same
var result1 = this.func(2);
var result2 = func.call(this, 2);
var result3 = func.apply(this, [2]);
position1 would not pass arguments other than the first to g()
If you do:
var position1 = 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.
I disagree with the author.
Think of the use-case for function-position. Most of the time I utilize function-position 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 position I create is not posable anymore, because it might not get all arguments it needs.
position that prevents position; 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 posable
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( pose( prepend("foo"), substr(0, 5) ) );
arr.map( pose( 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?
本文标签: JavaScript compose functionsStack Overflow
版权声明:本文标题:JavaScript compose functions - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745258232a2650230.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论