这个例子中的论点在哪里?

时间:2016-07-17 21:01:22

标签: javascript functional-programming

我目前正在阅读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参数在哪里?我错过了什么?

5 个答案:

答案 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()来达到同样的效果。