Functional programming 如何从功能上处理来自外部系统的状态?

Functional programming 如何从功能上处理来自外部系统的状态?,functional-programming,referential-transparency,architectural-patterns,Functional Programming,Referential Transparency,Architectural Patterns,我最近进入函数式编程领域,学习了几种以引用透明的方式处理某些副作用的方法: 对于可变状态,如更新变量 对于I/O,如从控制台读/写控制台 用于图形和输入设备事件等交互 但如今大多数“真实世界”应用程序都与外部系统(如web服务、数据库等)连接,这些系统可以由多个用户同时修改,它们具有状态,长时间运行的操作等。因此,情况不像上述类别中那样简单:询问系统实体的状态或试图控制实体的结果取决于其状态。此外,交互性也是一个要求:有一些用户可以随意点击的GUI,也许我们还必须自动对来自系统的更改做出反应

我最近进入函数式编程领域,学习了几种以引用透明的方式处理某些副作用的方法:

  • 对于可变状态,如更新变量
  • 对于I/O,如从控制台读/写控制台
  • 用于图形和输入设备事件等交互
但如今大多数“真实世界”应用程序都与外部系统(如web服务、数据库等)连接,这些系统可以由多个用户同时修改,它们具有状态,长时间运行的操作等。因此,情况不像上述类别中那样简单:询问系统实体的状态或试图控制实体的结果取决于其状态。此外,交互性也是一个要求:有一些用户可以随意点击的GUI,也许我们还必须自动对来自系统的更改做出反应


通过最大化纯函数的好处来设计和实现这类应用程序的模式是什么?或者以上的一些方法可以用我没有想到的方式来解决这个问题吗?这种语言(比如Java或Scala)没有强制执行100%的纯度,因此我对由实践经验支持的实用解决方案感兴趣。

我还没有以实际的方式完成这类事情,所以其他人希望能够做得更多。不过,我已经在Scala中编写了一个Android应用程序,它满足了您的一些要求;UI是交互式的,“状态”都存储在SQLite数据库中。数据库和UI都需要与Android框架接口,Android框架非常面向Java,因此不适合Scala或函数式编程

我所做的是采用类似MVC的设计,其中模型部分被实现为一组仅支持纯操作的ADT。这还有一个额外的优点,即模型代码完全独立于Android框架,因此可以在仿真器之外以我喜欢的任何方式进行测试

这就给我留下了控制器(视图是一个非常薄的层,主要是配置,采用Android的工作方式),加上“从数据库加载模型”和“将模型保存到数据库”的附加操作。作为Scala,我只是用不纯代码实现了这些部分,这些代码调用纯模型代码来执行实际的数据操作。在Haskell中,这些部分可能完全在IO monad[1]中

总而言之,这让我可以用纯功能的术语来思考我的问题域,而不必在与外部系统接口时“违反规则”。数据库层成为数据库模式和我用于数据模型的ADT之间的映射问题;处理这些操作可能失败的事实是控制器(启动DB操作)的责任,不会影响模型。控制器操作在概念上变得非常简单,如“按下此按钮时,将当前状态设置为在当前状态下调用函数的结果,然后更新显示表”。最终,这种不纯的“胶水”代码比实际的模型代码多得多,但我仍然认为这样做是一种胜利,因为我的应用程序的核心是对复杂结构化数据的操作,因此正确处理这一点是最棘手的。其余的大部分写起来都很乏味,而不是很难设计

当您的程序中有大量的计算时(不一定是大量的数据,只是您实际在上面计算了一些东西),这种方法就有效了。如果程序几乎完全是将不同的外部系统粘合在一起,那么您就不必获得那么多


[1] 请记住,IO monad不仅仅用于从控制台读/写。其结果受程序外部事物状态影响的建模操作正是IO monad的;通常,Haskellers会尽量避免在其程序的几乎整个过程中使用IO monad,如果它不是一直与外部系统交互(或者最好是即使是这样)。您可以对数据执行纯计算以响应来自“外部”的事件,或者甚至对响应外部事件需要执行的IO操作执行纯计算,如果这很复杂的话

但如今大多数“真实世界”应用程序都与外部系统(如web服务、数据库等)连接,这些系统可以由多个用户同时修改,它们具有状态,长时间运行的操作等。因此,情况不像上述类别中那样简单:询问系统实体的状态或试图控制实体的结果取决于其状态。此外,交互性也是一个要求:有一些用户可以随意点击的GUI,也许我们还必须自动对来自系统的更改做出反应

以交互方式,并发编辑共享状态只是状态单子的另一个示例。您可以使用镜头或其他抽象来对数据结构进行编辑,但在幕后,您所拥有的只是一个共享的全局状态

如果需要计算机级并发支持,可以使用并发结构(如STM var或MVar)来解决并发编辑的冲突。这意味着您将使用STM或IO单子

对于Haskell软件包来说,有很多为Hackage上的这类工作设计的一元环境的例子

a.k.a.似乎是一个有用且相关的抽象概念,似乎值得进一步探讨