什么是(功能性)响应式编程?
什么是(功能)响应式编程?
在纯函数式编程中,没有副作用。对于许多类型的软件(例如,任何涉及用户交互的软件),副作用在某种程度上是必要的。
在保留函数式样式的同时获得类似副作用的行为的一种方法是使用功能响应式编程。这是功能编程和响应式编程的结合。(您链接的维基百科文章是关于后者的。)
响应式编程背后的基本思想是,有某些数据类型表示随着时间的推移而变化的值。涉及这些随时间变化的值的计算本身也会随时间变化。
例如,您可以将鼠标坐标表示为一对随时间变化的整数值。假设我们有像下面这样的东西(这是伪代码):
x =
y =
在任何时间点,x和y都将具有鼠标的坐标。与非响应式编程不同,我们只需要进行一次赋值,x和y变量将自动保持“最新”。这就是为什么响应式编程和函数式编程很好地结合在一起的原因:响应式编程消除了对变量的改变的需要,同时仍然允许您完成许多变量改变所能实现的工作。
然后,如果我们根据这些结果进行一些计算,所得到的结果也将是随时间变化的值。例如:
minX = x - 16;
minY = y - 16;
maxX = x + 16;
maxY = y + 16;
在这个例子中,minX将始终比鼠标指针的x坐标小16。使用响应式感知的库,您可以这样说:
rectangle(minX, minY, maxX, maxY)
然后,一个32x32的矩形框将围绕鼠标指针绘制,并随着鼠标移动而跟踪。
这是一篇关于功能响应式编程的很好的论文。
那么,响应式编程是一种声明式编程吗?
功能型反应式编程是一种函数式编程的形式,而函数式编程是一种声明式编程的形式。
对,功能图形更有意义。我不确定为什么我在矩形绘制部分使用了命令式风格。
所以这就好像你在C语言中写了#define x mouse_x()?
不完全是这样。例如,如果我在C中使用您的宏调用sqrt(x),那只是计算sqrt(mouse_x())并给我一个double返回。在真正的功能响应式系统中,sqrt(x)将返回一个新的“随时间变化的double”。如果您尝试使用#define模拟FR系统,您基本上必须放弃变量,而使用宏。FR系统通常只在需要重新计算时才重新计算东西,而使用宏意味着您将不断重新计算所有东西,直到子表达式。
对于许多软件类型(例如,任何涉及用户交互的软件),副作用在某种程度上是必要的。“纯”的延迟功能编程的实现中存在许多副作用,而该范例的成功之一是将许多副作用排除在编程模型之外。我自己对功能用户界面的探索表明,它们也可以完全无副作用地进行编程。
与用户进行交互难道不是一种副作用吗?
“响应式编程消除了对变量的改变的需要,同时仍然允许您完成许多变量改变所能实现的工作。”对我来说,似乎正好相反。x随时间变化,这与改变它有什么不同?
这个描述让我想起了(可合成的)Verilog。如果有助于其他人理解这个概念,我想提一下。
x从不重新分配/更改。x的值是随时间变化的值序列。另一种看待它的方式是,x的值不是“正常”的值,比如一个数字,而是(概念上)一个以时间为参数的函数。(这有点过于简化。您无法创建可以预测鼠标位置等事物未来的时间值。)
我可能完全误解了您的回答,但是这里的想法是每个时刻更新每个变量吗?这不会对性能产生很大影响吗?例如,每个时刻更新x,y,minX,minY,maxX,maxY以及其他变量可能会变得很昂贵。但是我怀疑我完全误解了您的回答,所以会进行更多的研究。
'功能'和'响应'几乎互相抵消了吗?我看不出FRP是功能性的,它有最神奇的、不可预测的变量。没有副作用!你不知道一个变量会有什么值和为什么?
如果我们将这些神奇的整数随时间变化描述为时间的函数,那么这些函数中的一些将是纯函数,但其他函数则不是。鼠标指针位置的函数绝对不是纯函数,事实上它甚至不使用时间变量。所以我对这个想法持怀疑态度,因为您无法通过这种方式创建“纯”GUI。
是否实际上有像答案中的示例语言这样的语言可供尝试?
“Elm是一种编译为HTML、CSS和JS的功能性响应式编程(FRP)语言。” elm-lang.org
还有Bacon.js用于JavaScript。
我现在有大约两打标签页打开了FRP,这是我找到的最简洁的解释。您应该考虑更详细地介绍这个问题,比如写一本书。这是一个热门话题,正如我在对父帖的评论中指出的,所有现有的资料都是在对这个主题的天真观点下写的。
我想知道有多少人在阅读接受的答案后才发现这个更有用。而不知道接受的答案是为了让他们了解这个问题的。我认为FRP仍然是纯的原因是,给定相同的输入,整个系统将始终完全相同,无论之前发生了什么。因此,在矩形示例中,x和y是鼠标输入的函数。如果将鼠标放回到之前的位置,您将得到相同的系统,您不是将其突变回到先前的系统,而是重新计算相同的输入并获得相同的输出。另外,我认为在进行FRP时,您可以记住历史记录,因此所有变量都是像轨迹一样的。
保持状态是一种通过将函数分解为多个简单函数,并以状态值为条件来描述复杂函数的机制,从而简洁地描述大输入和输出空间上的复杂函数的方法。FRP回到了函数复杂且输入输出空间巨大的原始情况。这一直是可能的,因为整个状态空间可以卷入输入空间。但通常这样做是愚蠢的。
什么是(功能性)反应性编程?
功能性反应性编程(FRP)是一种用于描述程序行为的概念,其中程序的行为被表示为将时间值映射到输出值的数学函数。在FRP中,动态/演化的值是一等值,可以定义和组合,并可以传递到和从函数中。FRP的行为是由一些原语构建的,如常量(静态)行为和时间(类似于时钟),然后使用顺序和并行组合。为了解决离散现象,FRP引入了另一种类型(族)的“事件”,每个事件都有一系列事件发生。每个事件发生都有一个相关的时间和值。通过玩一些示例,可以得到构成所有行为和事件的组合词汇。为了确保模型的稳定性,使用指称语义的技术为整个模型提供了组成基础。指称语义意味着(a)每种类型都有一个相应的简单和精确的数学“意义”类型,并且(b)每个原语和运算符都有一个简单和精确的意义,作为成员意义的函数。
FRP的优点是它直接捕获动态值的演化,并且可以轻松处理连续演化的值。FRP的并发性是微粒级、确定性和连续的,这在推理方面非常重要。相比之下,命令式并发在推理方面增加了巨大的复杂性,因为它是非确定性交错的。FRP可以轻松处理并发,而不会遇到命令式并发所困扰的理论和实践混乱。
FRP的出现是为了解决命令式编程中对动态值的间接捕获和连续演化值的困难。通过将程序行为表示为时间的函数,FRP直接捕获动态值的演化,并且没有困难处理连续演化的值。此外,FRP的并发性是确定性的,这对于推理非常重要。
总之,FRP是一种用于描述程序行为的概念,其中程序的行为被表示为将时间值映射到输出值的数学函数。它直接捕获动态值的演化,并且具有确定性的并发性。FRP的出现是为了解决命令式编程中对动态值的间接捕获和连续演化值的困难。
什么是(函数式)反应式编程?
函数式反应式编程(FRP)是一种编程范式,它的出现源于以下原因:我们可以想象我们的程序就像一个电子表格,所有的变量都是单元格。如果电子表格中的任何一个单元格发生变化,依赖该单元格的所有单元格也会发生变化。这与FRP是一样的。现在想象一下,一些单元格会自动发生变化(或者更准确地说,来自外部世界的输入):在GUI情况下,鼠标的位置就是一个很好的例子。
然而,这个比喻忽略了很多细节。当你真正使用FRP系统时,这个比喻很快就会失效。首先,通常会尝试将离散事件(例如鼠标点击)建模为单元格变化。我只是把这个比喻放在这里是为了让你对FRP有个大致的了解。
有一个非常恰当的例子。理论知识很重要,也许有些人可以在没有基于例子的实践的情况下理解其中的含义,但是对我来说,我需要从它为我做了什么开始,而不是从它的抽象定义开始。最近我从Netflix的Rx演讲中得到的启示是,RP(或者Rx)使得这些“变化的值”成为一等公民,并且让你对它们进行推理,或者编写能够处理它们的函数。如果你愿意,你可以编写函数来创建电子表格或单元格。它还处理了值何时结束(消失),并且能够自动进行清理。
这个例子强调了事件驱动编程和反应式编程之间的区别,反应式编程只需要声明依赖关系,而不需要进行复杂的路由选择。