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