'foldp'违反FP的不可变状态原则吗? [英] Does 'foldp' violate FP's no mutable state principle?

查看:137
本文介绍了'foldp'违反FP的不可变状态原则吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从七周七种语言了解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 only 0 on the first evaluation of main
  • after the first evaluation of main it seems that the initial value of presses 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 Signals. Such a Signal can be based on other Signals 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 Signals to be based on the past value of Signals 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆