'foldp'违反FP的不可变状态原则吗? [英] Does 'foldp' violate FP's no mutable state principle?
问题描述
我从七周七种语言了解Elm 。下面的例子让我感到困惑:
$ p $ import键盘
main = lift asText(foldp(\dir press - > press + dir.x)0 Keyboard.arrows)
foldp
被定义为:
Signal.foldp:(a - > b - > b) - > b - >信号a - >信号b
在我看来:
- 累积器
的初始值在第一次评估时只是
0
main
- 在首次评估
main
之后,似乎初始值按下
就是函数(a - > b - > b)
或(\ dir press - >按+ dir.x)
在前面的评估中。
如果确实如此,那么这是否违反函数式编程原则,因为main现在保持内部状态(或至少 foldp
)?
当我在代码的多个位置使用 foldp
时,这是如何工作的?它是否保留多个内部状态,每次使用它一次?
我看到的唯一另一种选择是 foldp
(在这个例子中)从0开始计算,所以说,每次计算它时,都会按照 Keyboard.arrows
提供的整个历史记录进行折叠。这在我看来非常浪费,并且会在长时间运行时导致内存不足的情况。
我在这里错过了什么吗?
工作原理
是, foldp
保持一些内部状态。保存整个历史将是浪费,并没有完成。
如果您在代码中多次使用 foldp
,做不同的事情或者有不同的输入信号,那么每个实例将保持它自己的本地状态。例子:
import键盘
plus =(foldp(\dir press - > press + dir.x)0 Keyboard.arrows)
minus =(foldp(\dir press - >按-dir.x)0 Keyboard.arrows)
showThem pm = flow down(map asText [p ,m])
main = lift2 showThem plus减
但是如果使用结果信号从一个foldp两次,只有一个foldp实例将在您的编译程序中,所产生的更改将仅用于两个地方:
import keyboard
plus =(foldp(\dir press - > press + dir.x)0 Keyboard.arrows)
showThem pm = flow down(map asText [p,m ])
main = lift2 showThem plus plus
主要问题
如果确实如此,那么这不违反函数式编程原则,因为main现在保持内部状态(或至少
foldp
does)?
函数式编程没有每个人都使用的很好的规范定义。有许多函数式编程语言的例子允许使用可变状态。这些编程语言中的一部分向你展示了在类型系统中一个值是可变的(你可以看到Haskell的 state是一个
类型,它实际上取决于你的观点)。
但是什么是可变状态?什么是可变值?这是程序内部的一个值,它是可变的。也就是说,它可以改变。在不同的时间它可能是不同的东西。啊,但我们知道榆树如何随着时间的推移而改变价值观!这是一个 Signal
。
Elm中真正的 Signal
是一个可以改变的值时间,因此可以被看作变量,可变值或可变状态。只是我们非常严格地管理这个值,只允许在 Signal
s上进行一些精心挑选的操作。这样的 Signal
可以基于程序中的其他 Signal
s,或者来自库或来自外部世界(想象输入像 Mouse.position
)。谁知道外面的世界是如何产生这个信号的!因此,允许您自己的 Signal
s基于过去值 Signal
s实际上是可以的。
结论/ TL; DR
您可以看到 Signal
作为可变状态的安全包装。我们假设来自外部世界的信号(作为程序的输入)是不可预测的,但由于我们有这种只允许提升/采样/过滤/折叠的安全包装,所以您编写的程序是完全可预测的。副作用包含和管理,因此我认为它仍然是功能性编程。
I'm learning about Elm from Seven More Languages in Seven Weeks. The following example confuses me:
import Keyboard
main = lift asText (foldp (\dir presses -> presses + dir.x) 0 Keyboard.arrows)
foldp
is defined as:
Signal.foldp : (a -> b -> b) -> b -> Signal a -> Signal b
It appears to me that:
- the initial value of the accumulator
presses
is only0
on the first evaluation ofmain
- after the first evaluation of
main
it seems that the initial value ofpresses
is whatever the result of function(a -> b -> b)
, or(\dir presses -> presses + dir.x)
in the example, was on the previous evaluation.
If this is indeed the case, then isn't this a violation of functional programming principles, since main now maintains internal state (or at least foldp
does)?
How does this work when I use foldp
in multiple places in my code? Does it keep multiple internal states, one for each time I use it?
The only other alternative I see is that foldp
(in the example) starts counting from 0, so to say, each time it's evaluated, and somehow folds up the entire history provided by Keyboard.arrows
. This seems to me to be extremely wasteful and sure to cause out-of-memory exceptions for long run times.
Am I missing something here?
How it works
Yes, foldp
keeps some internal state around. Saving the entire history would be wasteful and is not done.
If you use foldp
multiple times in your code, doing distinct things or having distinct input signals, then each instance will keep it's own local state. Example:
import Keyboard
plus = (foldp (\dir presses -> presses + dir.x) 0 Keyboard.arrows)
minus = (foldp (\dir presses -> presses - dir.x) 0 Keyboard.arrows)
showThem p m = flow down (map asText [p, m])
main = lift2 showThem plus minus
But if you use the resulting signal from a foldp twice, only one foldp instance will be in your compiled program, the resulting changes will just be used in two place:
import Keyboard
plus = (foldp (\dir presses -> presses + dir.x) 0 Keyboard.arrows)
showThem p m = flow down (map asText [p, m])
main = lift2 showThem plus plus
The main question
If this is indeed the case, then isn't this a violation of functional programming principles, since main now maintains internal state (or at least
foldp
does)?
Functional programming doesn't have some great canonical definition that everybody uses. There are many examples of functional programming languages that allow for the use of mutable state. Some of these programming languages show you that a value is mutable in the type-system (you could see Haskell's State a
type as such, it really depends on your viewpoint though).
But what is mutable state? What is a mutable value? It's a value inside the program, that is mutable. That is, it can change. It can be different things at different times. Ah, but we know how Elm calls values at change over time! That's a Signal
.
So really a Signal
in Elm is a value that can change over time, and can therefore be seen as a variable, a mutable value, or mutable state. It's just that we manage this value very strictly by allowing only a few well-chosen manipulations on Signal
s. Such a Signal
can be based on other Signal
s in your program, or come from a library or come from the outside world (think of inputs like Mouse.position
). And who knows how the outside world came up with that signal! So allowing your own Signal
s to be based on the past value of Signal
s is actually ok.
Conclusion / TL;DR
You could see Signal
as a safety wrapper around mutable state. We assume that signals that come from the outside world (as input to your program) are not predictable, but because we have this safety wrapper that only allows lift/sample/filter/foldp, the program you write is otherwise completely predictable. Side-effects are contained and managed, therefore I think it's still "functional programming".
这篇关于'foldp'违反FP的不可变状态原则吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!