我目前正在阅读Mostly Adequate Guide on functional programming, chapter 2。
在那里,给出了以下示例
var getServerStuff = function(callback) {
return ajaxCall(function(json) {
return callback(json);
});
};
然后重构为:
var getServerStuff = ajaxCall;
在解释重构时,作者认为
return ajaxCall(function(json) {
return callback(json);
});
与
相同return ajaxCall(callback);
虽然我理解使用匿名函数的返回值调用ajaxCall
(这只是callback
的返回值),但我不知道重构版本应该如何工作 - json
参数在哪里?我错过了什么?
答案 0 :(得分:5)
问题已得到解答,但我认为一些粗体和删除线可以让您轻松查看代码转换。希望这个答案可以帮助那些正在努力想象这个问题的人。
你不会写......
var floor = function(x) { return Math.floor(x) }
相反,你会写......
var floor = Math.floor
...它将完全相同†。这被称为Eta Conversion,如果你重复两次,你会看到Brian如何在“充分适当的指南”中得到他的结果。
Eta转换的基本规则是:
function(x) { return f(x) } === f
......他们完全可以互换†
您可以在原始代码中使用相同的技术
var getServerStuff = function(callback) {
return ajaxCall(function(json) {
return callback(json)
})
}
首先看看......
return ajaxCall(function(json) { return callback(json) })
Eta转换说...
function(json) { return callback(json) } === callback
那么让我们看看整个代码与第一次eta转换的结果......
// first step
var getServerStuff = function(callback) {
return ajaxCall(function(json) {
return callback(json)
})
}
// eta converts to ...
var getServerStuff = function(callback) {
return ajaxCall(callback)
}
这种情况对我们来说应该很熟悉。另外一个eta转换将使我们进入最终的简化形式。我将再添加粗体,以便我们更好地看到它
Eta转换说...
function(callback) { return ajaxCall(callback) } === ajaxCall
// second step
var getServerStuff = function(callback) {
return ajaxCall(callback)
}
// eta converts to ...
var getServerStuff = ajaxCall
†出于所有意图和目的,它们是可以互换的。对FP的充分指南几乎不关心动态绑定或使用this
答案 1 :(得分:4)
这是一个功能性的重构,反之亦然。想象一下,你有一个函数ajaxCall
,它接受一个回调函数,它将与结果一起应用,例如。
ajaxCall(fun);
如果您要使用fun
替换function(json){ return fun(json); }
替换规则,则代码完全相同。因此你可以写:
ajaxCall(function(json){ return fun(json);});
就像我们包裹fun
我们可以包裹ajaxCall
一样,我们引入callback
作为fun
的替代,并将fun
作为参数。根据替换规则,它的代码完全相同:
(function(callback){
return ajaxCall(function(json){ callback(json);});
})(fun)
现在这与你在我的例子中实际调用它不一样,但是当我们拆分定义并调用时,你可能会看到与代码的相似之处:
var getServerStuff = function(callback) {
return ajaxCall(function(json){return callback(json);});
};
getServerStuff(fun);
如果您可以关注该链,那么您可以通过将参数中的函数包装器替换为ajaxCall来以相反的方式执行该操作:
var getServerStuff = function(callback) {
// return ajaxCall(function(json){return callback(json);});
return ajaxCall(callback);
};
getServerStuff(fun);
然后看到getServerStuff实际上也只是一个没有附加功能的包装器:
// var getServerStuff = function(callback){ return ajaxCall(callback);}
var getServerStuff = ajaxCall;
getServerStuff(fun);
然后getServerStuff只是一个别名,所以我们可以用值代替它:
ajaxCall(fun);
你去吧。就在你开始的地方。为此,您引入的变量不能影响受影响代码中使用的变量,并且替换函数必须支持与原始变量相同的arity。除此之外,它非常直接。
Jim Weirich通过标准重构规则进行重构来显示how to do the factorial function using only anonymous functions。它在Ruby中,但我没有遇到问题。在21:15,他展示了功能性重构,这是主题部分的重要内容。
答案 2 :(得分:2)
虽然我明白用匿名函数的返回值调用ajaxCall(这只是回调的返回值)
这实际上是倒退。
使用匿名函数作为参数调用 ajaxCall
。它可能最终调用该函数,然后该函数调用callback
。
提供所有函数名称通常会使这更容易理解。
function callback(json) {
console.log('I was called with ' + json);
}
function intermediate(json) {
return callback(json);
}
ajaxCall(intermediate);
此处,ajaxCall
已通过intermediate
。调用时,intermediate
将获取其单个参数并将其传递给callback
。
这个链条有点不必要,可以简化为:
function callback(json) {
console.log('I was called with ' + json);
}
ajaxCall(callback);
我希望有所帮助!
答案 3 :(得分:2)
getServerStuff
是一个高阶函数,它需要一个lambda(匿名函数)作为它的参数(它本身需要一个参数)。
ajaxCall
是一个更高阶的函数,它也需要一个lambda(它本身需要一个参数)。
数据在哪里(JSON)?让我们消除分散注意力的细节:
const hof = cb => hof2(x => cb(x)); // higher order function that expects a lambda
const hof2 = cb => cb(); // another HOF that expects a lambda
const inc = x => x + 1;
hof(inc); // NaN
这毫无意义。再次,数据在哪里?它们是从服务器异步获取的。所以我们需要调整一下我们的功能:
const inc = x => console.log(x + 1);
const hof = cb => hof2(x => cb(x));
const hof2 = cb => setTimeout(cb, 0, 2); // assumed `2` is the result of the async operation
hof(inc); // 3
最后,我们有一个HOF调用另一个从服务器获取数据的HOF,并将其lambda(回调)应用于该数据。
我们显然可以进一步简化这一点。我们只需要一个HOF,它需要一个期望数据的lambda,它由服务器异步提供:
const inc = x => console.log(x + 1);
const hof2 = cb => setTimeout(cb, 0, 2); // assumed `2` is the result of the async operation
hof2(inc); // 3
最后一行相当于ajaxCall(callback)
;
结论:这个例子很难理解,因为数据(json)是作为异步函数调用的结果而不是最初调用的代码传递的。
补充:如果你的代码中的所有函数都是一元的,那么它们只需要一个参数,那么就没有更多的东西 - 它是抽象的。这导致了功能组合和无点风格,这两个主题也在“适当指南”中有所描述。
答案 4 :(得分:1)
重点是
callback
等同于
json
不再有函数表达式,因此不需要参数callback
。而不是调用匿名函数(反过来用参数调用callback
并将其结果传回),你可以直接调用str()
来达到同样的效果。