副作用的函数参数

时间:2017-03-02 14:00:12

标签: javascript functional-programming

我可以使用参数来保持函数的纯粹吗?

示例:

const activateAUser = (user, log) => {
  const newUser = {...user, active: true};

  log("User activated");

  return newUser;
}

activateAUser({active: false, name: 'John'}, console.log);

在这种情况下,我们可以说,activateAnUser函数是纯粹的吗?

4 个答案:

答案 0 :(得分:2)

而不是争论纯度的确切定义'是的,我将提供另一种观点:即你的例子是次优的。为什么要将用户激活与日志记录混为一谈?您试图撰写两个不相关的内容,但您已完成向后的构图。

var log = f => (...args) => {
  var result = f(...args);
  console.log(result);
  return result;
};

var activateUser = log(user => ({...user, active: true}));

现在activateUser(或者至少是参数函数)绝对是纯粹的,log绝对不是,而且我们已经支持了一个无启动的论点。每当你发现自己在辩论(内部或外部)这些事情时,都需要重新考虑你的设计。

答案 1 :(得分:1)

问自己一个简单的问题:我可以用它的返回值替换函数调用而不改变程序的行为吗?如果你回答这个问题是肯定的,那就是纯粹的,否则就是不纯的功能。

x = as.double(1:1e8) system.time(x[1] <- 100) y = sum(x) system.time(x[1] <- 200) ## Fast (takes 0s), after calling sum foo = function(x) { return(sum(x)) } y = foo(x) system.time(x[1] <- 300) ## Slow (takes 0.35s), after calling foo 本质上是不纯的,因为它改变了行为而没有提供有意义的返回值 - 它只返回console.log。因此,undefined也是不纯的。

只需使用activateAUser分隔记录:

&#13;
&#13;
tap
&#13;
&#13;
&#13;

答案 2 :(得分:1)

自由变量和常量不被认为是不纯的

  

我可以使用参数来保持函数的纯粹吗?

您是否建议您需要log参数来保持您的功能纯净?以下是完全纯粹的程序

const point2degrees = (x,y) =>
  Math.atan2(y,x) * 180 / Math.PI
  
console.log(point2degrees(3,3)) // 45•
console.log(point2degrees(1,2)) // 63•

仅仅因为我们的函数引用了自由变量和常量Math.atan2*180/Math.PI并没有使它变得不那么纯粹。换句话说,去写这个

// no, bad
const point2degrees = (x,y,atan,pi) =>
  atan(y,2) * 180 / pi

// this is ludicrous
console.log(point2degrees(3, 3, Math.atan2, Math.PI))

副作用不纯

但是,如果我们向控制台写了一些东西, 不纯 - 日志记录是副作用,应该尽可能包含它。

想象一个简单的函数add,在我方便的库中。接下来,假设我们在程序中的其他地方使用它

const add = (x,y) => {
  const sum = x + y
  console.log("debug sum", sum)
  return sum
}

const result = [1,2,3].reduce(add, 0)
// "debug sum 1"
// "debug sum 3"
// "debug sum 6"

console.log(result)
// 6

真是个噩梦!我不想在我的控制台中输出所有debug个输出。为什么我不能在没有日志记录的情况下使用add函数? - 或换句话说,add的预期效果是将两个数字相加,但它也有一个副作用也写入控制台。 - add不纯函数,因为它有副作用。

  

在这种情况下,我们可以说,activateAnUser函数是纯粹的吗?

没有。 activateAnUser具有登录控制台的副作用。没有多少添加的参数可以帮助您远离它。

如何修复

我们将使用IO monad的非常基本的实现。 IO可以有效地驯服令人反感的,不纯的代码,并允许我们以纯粹的方式使用它。这是一个非常简单的单子,所以在阅读它时要耐心等待,你应该能够理解它。

// IO monad
const IO = f => ({
  unsafePerformIO: f,
  chain: g => g(f())
})

// IO-safe log utility
const log = x => IO($ => (console.log(x), x))

// remove the log call from your function; that is not its job
const activateAnUser = (user, log) => {
  // i used Object.assign here to keep this snippet runnable
  // you can use {...user, active: true} in your code
  return Object.assign({}, user, {active: true})
}

// your main program
// activate a user, then display it
const main = user =>
  IO($ => activateAnUser(user)).chain(log)

// sample data
const user = { name: "Sławomir" }

// PURE above this line
// IMPURE below this line; unsafePerformIO logs to the console
main(user).unsafePerformIO()

了解更多关于天才教授在JavaScript中实现的IO Monad的信息。

答案 3 :(得分:0)

假设log做了什么,它仍然不是纯粹的功能。创建日志条目 是函数的副作用,虽然是一个所需的副作用,因此 在调用一次,缓存和重用结果以及调用它之间存在差异100次相同的论点。