如果将函数作为参数,函数是否立即不纯?


17

由于输入参数的纯度直到运行时才是未知的,如果将函数作为输入参数,该函数是否立即被认为是不纯的?

相关:如果一个函数应用了一个在函数外部定义的纯函数,但没有作为参数传递,那么它是否满足纯函数的要求,即输出是否仅取决于输入,它是否仍然是纯函数?

对于上下文,我正在用JavaScript编写功能代码。


作为一个微不足道的反例,请考虑:foo = function(function bar){ print(bar.toString()) }
大卫说恢复莫妮卡

1
@DavidGrinberg,我认为这不是一个反例,实际上凸显了一个更大的问题。如果您具有可以重写的函数,并且不能保证实现没有副作用,那么您就不能保证大多数采用对象并调用其方法的函数都是纯函数。也许酒吧的toString()从磁盘上删除了一些文件?
约书亚·泰勒

3
@DavidGrinberg但是,我认为您的想法很正确。 foo = function(function bar) { return 3; } 纯函数,并以函数作为参数。
约书亚·泰勒

@JoshuaTaylor公平的观点,我没有想到。但是您已经基本上解决了这个问题。作为一种替代解决方案,只需调用“ root” toString()(即您可以在Java的Object上找到的根)。
大卫说恢复莫妮卡

Answers:


22

只要在函数中使用的所有值都仅由其参数定义,它就是一个纯函数。

每次输入相同输入时输出都相同的方面受参数是否为纯参数控制。如果假设参数(如函数参数)也是纯的,那么它就是纯的。

在Javascript之类的不要求纯净的语言中,这意味着可以通过调用作为参数传递的不纯函数使原本纯净的函数具有不纯行为。

这实际上意味着,对于不要求纯净的语言(即几乎全部),不可能定义一个纯函数来调用作为参数传递的函数。尽可能将它们写为纯函数并将其推论为纯函数仍然很有用,但是您必须谨慎行事,因为如果传递了错误的参数,就将破坏纯函数的假设。

根据我的实践经验,这通常没什么大不了的-我发现很少将不纯函数用作纯函数的函数参数。


关于您的陈述,“只要函数中使用的所有值仅由其参数定义,它就是纯函数”。在常量的情况下会发生什么?如果我有一个function areaOfCircle r => Math.Pi * r * rareaOfCircle那不是纯粹的,因为它不仅仅使用参数吗?
大卫·阿诺

2
@DavidArno这是一个公平的观点。根据引用透明性,引用静态外部值与对其进行硬编码没有什么不同,因此它仍然是纯净的。
丹妮丝2015年

1
“这意味着可以使纯函数具有不纯行为”-根据定义,纯函数不能具有不纯行为。您误以为f(f2)调用的函数f2不会可传递地依赖于任何f2依赖的东西。可能调用任意传入函数的函数并非纯函数。
user2357112支持Monica15年

2
@Daenyth:更好,但仍假定该函数必须调用传入的函数。可能像一样function compose(f, g) {return function h(x) {return f(g(x));};},尽管以函数为参数,它仍然是纯净的。
user2357112支持Monica 2015年

1
“我发现很少将不纯函数用作纯函数的函数参数。” -不是一种功能语言,但是某些C ++库函数具有特定的警告,这些警告要求谓词参数必须是(纯近似的)纯净值。因此从某种意义上说,这不仅罕见,而且永远不会有效发生。但是从另一种意义上说,他们必须禁止这样做的事实是因为人们有时确实这样做。例如,他们想要传递一个find不纯净的谓词,该谓词会针对遇到的第三个匹配项或某些废话返回“ true”。
史蒂夫·杰索普

19

由于输入参数的纯度直到运行时才是未知的,如果将函数作为输入参数,该函数是否立即被认为是不纯的?

否。反例:

function pure(other_function) {
    return 1;
}

没关系 other_function函数是纯函数,不纯函数还是。该pure函数是纯函数。

其他反例:

function identity(x) {
    return x;
}

即使x是不纯函数,该函数也是纯函数。,无论您重复通话多少次,identity(impure_function)都将始终返回impure_function。是否identity(impure_function)()总是返回相同的东西并不重要。函数的返回值不影响其纯度。


通常,如果一个函数可能调用一个函数作为参数传递,则它不是纯函数。例如一个功能function call(f) {f();}不是纯,因为即使它没有提及任何全局或可变状态,f也可能会alert引起可见的副作用。

如果一个函数将函数作为参数,但是它没有调用它们或导致它们被调用,那么它可以是纯函数。如果它做其他不纯净的事情可能仍然不纯净。例如,function f(ignored_function) {alert('This isn't pure.');}即使它从不调用,它也是不纯的ignored_function


4
这种反应似乎过于腐。我们可以从问题中推断出关注点在于调用的函数参数。可以/确实可以将其他函数作为参数而不调用它们的函数的存在并不影响这个问题。
walpen

13
@walpen:这个问题没有提到调用参数。没有理由假设发问者甚至意识到一个函数可能会在不调用另一个函数的情况下将其作为输入。指出这样的隐藏假设很重要,而不仅仅是假设您是要假设它们。
user2357112支持Monica15年

12

由于输入参数的纯度直到运行时才是未知的,如果将函数作为输入参数,该函数是否立即被认为是不纯的?

从技术上讲,是的,除非您的语言中有某种方式可以确保输入函数也是纯函数。

如果函数应用了在函数外部定义的纯函数,但未作为参数传递,那么它是否满足无副作用标准且输出仅取决于输入,是否仍是纯函数?

是。因此,让我们专注于这里重要的事情。调用纯函数或不调用函数本身并没有用。纯函数很有用,因为对于任何输入产生相同的输出,而不依赖于状态或没有副作用是一组非常有用的属性。这意味着一旦运行了函数,您就可以“记住”该输入的答案,并且它将始终为真。您也不需要再次运行该函数即可产生副作用。可以运行与其他功能该功能并行(或故障),知道他们不会有任何隐藏的互动,表现不好。

如果该函数使用其他纯只读函数来执行其工作,则无论这些函数如何引用它们,这些有用的属性仍将保留。


5

正如Telastyn所说:从技术上讲,是的,除非您的语言中有某种方法可以保证输入函数也是纯函数。

那不是假设,确实有很好的方法可以保证这一点。至少使用强类型语言。

您可以在JavaScript中编写的如此纯函数

function foo(f) {
   return f(1) + 2;
}

可以直接翻译为Haskell:

foo :: (Int -> Int) -> Int
foo f = f 1 + 2

现在,在JavaScript中,您可以做诸如

js> foo (function(x) {console.log("muharhar"); return 0})
muharhar
2

在Haskell中这是不可能的。原因是,类似副作用的东西console.log()必须始终具有结果类型IO something,而不仅仅是一个结果类型something

GHCi> foo (\x -> print "muarhar" >> return 0)

<interactive>:7:12:
    Couldn't match expected type ‘Int’ with actual type ‘IO b0’
    In the expression: print "muarhar" >> return 0
    In the first argument of ‘foo’, namely
      ‘(\ x -> print "muarhar" >> return 0)’
    In the expression: foo (\ x -> print "muarhar" >> return 0)

为了对此表达式进行foo类型检查,我们需要提供类型签名

foo :: (Int -> IO Int) -> Int

但是事实证明我不能再实现它了:因为参数函数包含IO在结果中,所以我不能在中使用它foo

<interactive>:8:44:
    Couldn't match expected type ‘Int’ with actual type ‘IO Int’
    In the first argument of ‘(+)’, namely ‘f 1’
    In the expression: f 1 + 2

我可以在其中使用IO操作的唯一方法foo是,如果的结果本身foo具有类型IO Int

foo :: (Int -> IO Int) -> IO Int
foo f = do
   f1 <- f 1
   return (f1 + 2)

但是从签名的角度来看foo,这显然不是一个纯函数。


1
在您说“不可能”之前,请看一下unsafeIO:-)
Bergi,2015年

2
@Bergi:实际上不是Haskell的一部分,而是其外部函数接口的一部分:允许断言用其他某种语言定义的函数是纯函数,Haskell编译器显然不能从类型签名中推断出该函数,因为其他语言通常没有这样的事情IO。顺便说一句,它也可以通过将副作用隐藏在“纯”函数中而引起混乱,但这在Haskell中确实不安全,因为实际上并没有一种可靠的方法来指定纯函数的评估顺序。
大约

对,是真的。但是,我认为我已经看到它也被用来“隐藏” “纯”功能中的有益副作用,因为以前必须确定该方法的安全性。
Bergi 2015年

@Bergi您通常不应该使用unsafeIO;那是逃避类型系统保证的最后手段逃生舱口,因此您的建议不是一个好主意。
Andres F.

0

不它不是。

如果传递的函数不纯并且您的函数调用传递的函数,则您的函数将被视为不纯。

纯/不纯关系有点像JS中的同步/异步。您可以从不纯净的地方自由地使用纯代码,但反之则不能。


此答案未添加本章中尚未解释的任何内容……请在重述内容之前查看先前的答案:)
Andres F.

同步/异步类比怎么样?
Bobby Marinoff '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.