Scala 副作用是纯函数中找不到的吗?

Scala 副作用是纯函数中找不到的吗?,scala,haskell,f#,functional-programming,purely-functional,Scala,Haskell,F#,Functional Programming,Purely Functional,可以肯定地说,以下二分法适用: 每个给定函数都是 或纯 还是有副作用 如果是这样的话,(函数的)副作用就是在纯函数中找不到的任何东西。这在很大程度上取决于您选择的定义。可以肯定地说,函数是纯函数还是不纯函数。纯函数总是返回相同的结果,并且不修改环境。一个不纯函数在重复执行时会返回不同的结果(这可能是由于对环境做了一些事情造成的) 所有杂质都有副作用吗?我不会这么说-函数可以依赖于它执行的环境中的某些东西。这可能是读取一些配置、GPS位置或从互联网读取数据。这些都不是真正的“副作用”,因为它对

可以肯定地说,以下二分法适用:

每个给定函数都是

  • 或纯
  • 还是有副作用

如果是这样的话,(函数的)副作用就是在纯函数中找不到的任何东西。

这在很大程度上取决于您选择的定义。可以肯定地说,函数是纯函数还是不纯函数。纯函数总是返回相同的结果,并且不修改环境。一个不纯函数在重复执行时会返回不同的结果(这可能是由于对环境做了一些事情造成的)

所有杂质都有副作用吗?我不会这么说-函数可以依赖于它执行的环境中的某些东西。这可能是读取一些配置、GPS位置或从互联网读取数据。这些都不是真正的“副作用”,因为它对世界没有任何影响

我认为有两种不同的杂质:

  • 输出杂质是指函数对世界做了一些事情。在Haskell中,这是使用单子建模的-一个不纯净的函数
    a->b
    实际上是一个函数
    a->mb
    ,其中
    M
    捕获了它对世界所做的其他事情

  • 输入杂质是指函数需要环境中的某些东西时。不纯函数
    a->b
    可以建模为函数
    ca->b
    ,其中类型
    C
    从函数可以访问的环境中捕获其他东西


单子和输出杂质当然更为人所知,但我认为输入杂质同样重要。我写了一个我称之为coeffects的函数,所以我认为这可能是一个有偏见的答案。

对于一个纯函数,它必须:

  • 不受副作用的影响(即,对于相同的参数,始终返回相同的值)
  • 不会引起副作用

  • 但是,你们看,这定义了功能纯度的属性或无副作用。您正试图应用反向逻辑来推断使用纯函数的副作用定义,这在逻辑上应该是可行的,但实际上副作用的定义与函数纯度无关。

    定义函数纯度的方法是
    f
    ∀x∀yx=y⇒ f x=f y,即给定相同的参数,函数返回相同的结果,或者保持相等

    这不是人们通常所说的“纯函数”;它们通常意味着“纯”,因为“没有副作用”。我还没有弄清楚如何限定“副作用”(欢迎评论!),所以我没有什么要说的

    尽管如此,我仍将探讨纯度的概念,因为它可能提供一些相关的见解。我不是这里的专家;这主要是我的闲话。不过,我确实希望它能引发一些有见地的(和纠正性的)评论

    为了理解纯洁,我们必须知道我们谈论的是什么样的平等。什么是
    x=y
    意思,什么是
    fx=fy
    意思

    一种选择是Haskell语义平等。也就是说,Haskell赋予其术语的语义相等。据我所知,Haskell没有官方的指称语义学,但提供了一个合理的标准,我认为社区或多或少都同意这个标准。当Haskell说它的函数是纯函数时,这就是它所指的等式

    另一种选择是通过派生
    Eq
    类来定义用户定义的等式(即
    (==)
    )。这在使用指称设计时是相关的——也就是说,我们将自己的语义分配给术语。通过这种选择,我们可以意外地写出不纯净的函数;Haskell不关心我们的语义

    我将Haskell语义等式称为
    =
    ,用户定义的等式称为
    =
    。另外,我假设
    ==
    是一个相等关系-对于
    ==
    的某些实例,例如
    Float
    ,这并不成立

    当我使用
    x==y
    作为命题时,我真正的意思是
    x==y=True∨ x==y=⊥x==y::Bool
    ⊥ :: Bool
    。换句话说,当我说
    x==y
    为真时,我的意思是如果它的计算结果不是⊥ 然后它计算为真

    如果根据Haskell的语义,
    x
    y
    是相等的,那么根据我们可能选择的任何其他语义,它们是相等的

    证明:如果
    x=y
    那么
    x==y≡ x==x
    x==x
    是真的,因为
    =
    是纯的(根据
    =
    )和自反的

    同样,我们可以证明
    ∀F∀x∀yx=y⇒ fx==fy
    。如果
    x=y
    那么
    fx=fy
    (因为
    f
    是纯的),因此
    fx==fy≡ fx==fx
    fx==fx
    是真的,因为
    =
    是纯的和自反的

    下面是一个愚蠢的例子,说明如何打破用户定义的等式的纯度

    data Pair a = Pair a a
    
    instance (Eq a) => Eq (Pair a) where
      Pair x _ == Pair y _ = x == y
    
    swap :: Pair a -> Pair a
    swap (Pair x y) = Pair y x
    
    现在我们有:

    Pair 0 1 == Pair 0 2
    
    但是:

    注意:
    ,(第01对=第02对)
    因此我们不能保证我们对
    (==)
    的定义是正确的

    一个更令人信服的例子是考虑<代码>数据。如果

    x,y,z::设置A
    ,则您希望它保持不变,例如:

    x == y ⇒ (Set.union z) x == (Set.union z) y
    
    特别是当
    Set.fromList[1,2,3]
    Set.fromList[3,2,1]
    表示同一个集合,但可能有不同的(隐藏的)表示(Haskell的语义并不等同)。也就是说,我们希望确保
    ∀根据
    (==)
    ,z Set.union z
    是纯的

    这是我玩过的一种类型:

    newtype Spry a = Spry [a]
    
    instance (Eq a) => Eq (Spry a) where
      Spry xs == Spry ys = fmap head (group xs) == fmap head (group ys)
    
    Spry
    是一个相邻元素不相等的列表。示例:

    Spry [] == Spry []
    Spry [1,1] == Spry [1]
    Spry [1,2,2,2,1,1,2] == Spry [1,2,1,2]
    
    鉴于此,什么是pu
    Spry [] == Spry []
    Spry [1,1] == Spry [1]
    Spry [1,2,2,2,1,1,2] == Spry [1,2,1,2]
    
    f e
    
    f x = x; x
    f (print 3)
    
    3
    
    3
    3