【问题标题】:What's the status of current Functional Reactive Programming implementations?当前功能响应式编程实现的状态如何?
【发布时间】:2012-10-31 18:31:27
【问题描述】:

我试图在 Haskell 中可视化一些简单的自动物理系统(例如钟摆、机械臂等)。 通常这些系统可以用如下等式来描述

df/dt = c*f(t) + u(t)

其中u(t) 代表某种“智能控制”。这些系统看起来非常适合功能响应式编程范式。

于是我拿起了 Paul Hudak 的《哈斯克尔表达学派》一书, 并发现那里提供的领域特定语言“FAL”(用于功能动画语言)实际上对于我的简单玩具系统非常有效(尽管某些功能,特别是integrate,对于有效使用来说似乎有点懒惰,但是容易修复)。

我的问题是,对于当今更高级甚至实际的应用程序,有什么更成熟、最新、维护良好、性能调整的替代方案?

This wiki page 列出了 Haskell 的几个选项,但我不清楚以下几个方面:

  1. “反应式”的状态,Conal Eliott 的项目(据我了解)是这种编程范式的发明者之一,看起来有点陈旧。我喜欢他的代码,但也许我应该尝试其他更新的替代方案?在语法/性能/运行时稳定性方面,它们之间的主要区别是什么?

  2. 引用 2011 年 survey 的第 6 节,“... FRP 实现在性能方面仍然不够高效或可预测,无法在需要延迟保证的领域中有效使用.. .”。尽管调查提出了一些有趣的可能优化,但考虑到 FRP 已经存在超过 15 年这一事实,我的印象是,这个性能问题可能是非常,甚至至少在内部很难解决一些年。这是真的吗?

  3. 该调查的同一作者在其blog 中谈到了“时间泄漏”。这个问题是 FRP 独有的,还是我们在使用纯非严格语言编程时通常遇到的问题?如果性能不够好,您是否曾经发现很难随着时间的推移稳定基于 FRP 的系统?

  4. 这仍然是一个研究级项目吗?工厂工程师、机器人工程师、金融工程师等人是否真的在使用它们(以适合他们需要的任何语言)?

虽然我个人更喜欢 Haskell 实现,但我愿意接受其他建议。例如,拥有一个 Erlang 实现会特别有趣 --- 这样就很容易拥有一个智能、自适应、自学习的服务器进程!

【问题讨论】:

    标签: haskell functional-programming erlang reactive-programming


    【解决方案1】:

    目前主要有两个实用的 Haskell 库用于函数式反应式编程。两者都由一个人维护,但也收到了其他 Haskell 程序员的代码贡献:

    • Netwire 专注于效率、灵活性和可预测性。它有自己的事件范式,可以用于传统 FRP 无法工作的领域,包括网络服务和复杂的模拟。风格:应用和/或箭头。最初的作者和维护者:Ertugrul Söylemez(这是我)。

    • reactive-banana 建立在传统的 FRP 范例之上。虽然使用它很实用,但它也可以作为经典 FRP 研究的基础。它的主要重点是用户界面,并且有一个现成的 wx 界面。风格:适用。初始作者和维护者:Heinrich Apfelmus。

    您应该尝试这两种方法,但根据您的应用,您可能会发现其中一种更适合。

    对于游戏、网络、机器人控制和模拟,您会发现 Netwire 非常有用。它为这些应用程序提供了现成的wires,包括各种有用的微分、积分和许多用于透明事件处理的功能。有关教程,请访问我链接的页面上的 Control.Wire 模块的文档。

    对于图形用户界面,目前最好的选择是响应式香蕉。它已经有一个 wx 接口(作为一个单独的库 reactive-banana-wx),并且 Heinrich 在此上下文中写了很多关于 FRP 的博客,包括代码示例。

    回答您的其他问题:FRP 不适用于您需要实时可预测性的场景。这主要归功于 Haskell,但不幸的是,FRP 很难在低级语言中实现。一旦 Haskell 本身成为实时就绪,FRP 也将到达那里。从概念上讲,Netwire 已为实时应用程序做好了准备。

    时间泄漏不再是一个真正的问题,因为它们在很大程度上与一元框架有关。实际的 FRP 实现根本不提供单子接口。 Yampa 已经开始了这个,Netwire 和 reactive-banana 都建立在这个基础上。

    我知道目前没有使用 FRP 的商业或其他大型项目。图书馆已经准备好了,但我认为人们还没有——还没有。

    【讨论】:

    • 很好的答案,谢谢.. 在您的库之上实现一些强化学习算法将是一个有趣的练习。
    • 值得注意的是,最近一个用 Haskell 编写的独立游戏 (Nikki and the Robots) 决定使用 FRP。
    【解决方案2】:

    虽然已经有一些很好的答案,但我将尝试回答您的具体问题。

    1. 由于时间泄漏问题,reactive 不适用于严肃的项目。 (见#3)。当前设计最相似的库是 reactive-banana,它的开发灵感来自于响应式,并与 Conal Elliott 进行了讨论。

    2. 虽然 Haskell 本身不适合硬实时应用程序,但在某些情况下可以将 Haskell 用于软实时应用程序。我不熟悉当前的研究,但我不认为这是一个无法克服的问题。我怀疑像 Yampa 这样的系统,或者像 Atom 这样的代码生成系统,可能是解决这个问题的最佳方法。

    3. “时间泄漏”是可切换 FRP 特有的问题。当系统无法释放旧对象时会发生泄漏,因为如果将来某个时间发生切换,它可能需要它们。除了内存泄漏(可能非常严重)之外,另一个后果是,当切换发生时,系统必须暂停,同时遍历旧对象链以生成当前状态。

    Yampa 和旧版本的响应式香蕉等不可切换的 frp 库不会受到时间泄漏的影响。可切换的 frp 库通常采用以下两种方案之一:要么它们具有特殊的“创建单子”,在其中创建 FRP 值,要么它们使用“老化”类型参数来限制可以发生切换的上下文。 elerea(可能还有 netwire?)使用前者,而最近的反应性香蕉和葡萄柚使用后者。

    “可切换frp”是指实现Conal 的函数switcher :: Behavior a -> Event (Behavior a) -> Behavior a 或相同语义的函数。这意味着网络的形状可以在运行时动态切换。

    这并不真正与@ertes 关于单子接口的声明相矛盾:事实证明,为Event 提供Monad 实例会使时间泄漏成为可能,并且使用上述任何一种方法都不再可能定义等效的 Monad 实例。

    最后,尽管 FRP 还有很多工作要做,但我认为一些较新的平台(reactive-banana、elerea、netwire)足够稳定和成熟,您可以从它们构建可靠的代码。但是您可能需要花费大量时间了解细节,才能了解如何获得良好的性能。

    【讨论】:

    • 关于基于箭头的库(Yampa、netwire),它们也是可切换的。原因是箭头内置了老化,您实际上无法摆脱它。 (作为流转换器,箭头与其输入流的开始时间无关。)
    • @HeinrichApfelmus:这是一个有趣的观点。我通常认为基于箭头的库不像 elerea/grapefruit/current-reactive-banana 那样可切换。我认为他们的切换更接近之前版本的响应式香蕉所要求的。不过,这只是一种直觉,我什至没有考虑足够的想法来描述我的意思。
    • @DanBurton 谢谢,我试图记住这个名字,但没有成功。我同意应将钠视为现代 FRP 库,尽管它不像反应香蕉那样受欢迎。
    • 虽然正在进行的讨论有点难以理解,但它似乎表明软实时系统确实是可能的,只要 GC 时间可以以某种方式限制。无论如何,感谢您的出色回答。
    【解决方案3】:

    我将列出 Mono 和 .Net 空间中的几项,以及我不久前发现的 Haskell 空间中的一项。我将从 Haskell 开始。

    榆树 - link

    根据其网站的描述:

    Elm 旨在让前端 Web 开发更愉快。它 引入了一种新的 GUI 编程方法,可以纠正 HTML、CSS 和 JavaScript 的系统性问题。榆树让你 快速轻松地使用视觉布局,使用画布,管理 复杂的用户输入,摆脱回调地狱。

    它有自己的FRP 变体。从玩它的例子来看,它似乎很成熟。

    反应式扩展 - link

    首页的描述:

    Reactive Extensions (Rx) 是一个用于编写异步的库 和使用可观察序列和 LINQ 风格的基于事件的程序 查询运算符。使用 Rx,开发人员表示异步数据 带有 Observables 的流,使用 LINQ 查询异步数据流 运算符,并参数化异步数据中的并发性 流使用调度程序。简单地说,Rx = Observables + LINQ + 调度程序。

    Reactive Extensions 来自 MSFT,并实现了许多简化事件处理的优秀运算符。几天前还是open sourced。它非常成熟并用于生产;在我看来,对于 Windows 8 API,它会是比 TPL 库提供的更好的 API;因为 observables 既可以是热的也可以是冷的,也可以是重试/合并等,而任务总是代表热的或已完成的计算,这些计算要么正在运行,要么出现故障,要么已完成。

    我已经使用 Rx 为异步编写了服务器端代码,但我必须承认,用 C# 编写函数式可能有点烦人。 F# 有几个封装器,但很难跟踪 API 开发,因为该组相对封闭,不像其他项目那样由 MSFT 推广。

    它的开源伴随着它的 IL-to-JS 编译器的开源,所以它很可能与 JavaScript 或 Elm 一起工作。

    您可以使用消息代理(如 RabbitMQ 和 SocksJS)将 F#/C#/JS/Haskell 很好地绑定在一起。

    Bling UI 工具包 - link

    首页的描述:

    Bling 是一个基于 C# 的库,用于轻松编程图像、动画、 Microsoft 的 WPF/.NET 上的交互和可视化。金光闪闪是 面向设计技术人员,即有时 程序,以帮助快速制作丰富的 UI 设计理念的原型。 学生、艺术家、研究人员和爱好者也会发现 Bling 作为快速表达想法或可视化的工具很有用。 Bling 的 API 和结构针对快速编程进行了优化 抛弃代码,而不是仔细地对生产进行编程 代码。

    免费LtU-article

    我已经对此进行了测试,但没有在客户项目中使用它。它看起来很棒,有很好的 C# 运算符重载,形成值之间的绑定。它使用 WPF/SL/(WinRT) 中的依赖属性作为事件源。它的 3D 动画在合理的硬件上运行良好。如果我最终从事需要可视化的项目,我会使用它;可能会将其移植到 Windows 8。

    ReactiveUI - link

    Paul Betts,之前在 MSFT,现在在 Github,编写了这个框架。我已经非常广泛地使用它并且喜欢这个模型。它比 Blink 更解耦(本质上与使用 Rx 和它的抽象) - 使用它更容易对代码进行单元测试。 Windows的github git客户端就是用这个写的。

    评论

    响应式模型的性能足以满足大多数性能要求高的应用程序。如果您正在考虑硬实时,我敢打赌大多数 GC 语言都有问题。 Rx,ReactiveUI 创建了一些需要 GC 的小对象,因为这就是创建/处理订阅的方式,并且中间值在回调的反应性“monad”中进行处理。一般来说,在.Net上,我更喜欢反应式编程而不是基于任务的编程,因为回调是静态的(在编译时知道,没有分配),而任务是动态分配的(未知,所有调用都需要一个实例,垃圾创建) - 并且 lambdas 编译成编译器生成的类。

    显然 C# 和 F# 是经过严格评估的,因此这里的时间泄漏不是问题。 JS也是一样。不过,这可能是可重放或缓存的可观察对象的问题。

    【讨论】:

    • 感谢您的出色回答。我喜欢 Haskell FRP 实现的一件事是,它们似乎让我能够清晰地解耦控件u(t) 的计算和f(t) 的模拟。 F# 实现就是这种情况吗?
    • 我猜你可以说这两个函数在时间上是解耦的,是的。不过,它们可能在逻辑上没有解耦。 ;)
    • 据我所知,Reactive Extensions 和其他更精致的 UI 包(以及 Haskell 之外的所有东西,事实上)只使用事件语义——也就是说,它们有一个您可以打开事件的概念,但不是可以方程交互的连续时间信号的概念。我认为,对于构建 gui,这很好。但是,对于构建模拟和模型,这可能是不幸的。
    • 您是否暗示所有函数式反应式编程库实现都需要连续地而不是离散地建模时间?我找到了一篇名为“Process Algebra with Timing: Real Time and Discrete Time”的论文——这是理解你在说什么的一个很好的起点吗?
    • 我并不是说所有人都需要——有些需要,有些不需要。但那些会更适合某些任务,而那些不会更适合其他任务......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-30
    • 1970-01-01
    • 2018-06-07
    • 1970-01-01
    • 2011-08-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多