我可以使用参数来保持函数的纯粹吗?
示例:
const activateAUser = (user, log) => {
const newUser = {...user, active: true};
log("User activated");
return newUser;
}
activateAUser({active: false, name: 'John'}, console.log);
在这种情况下,我们可以说,activateAnUser
函数是纯粹的吗?
答案 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
分隔记录:
tap
&#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次相同的论点。