我最近进入了函数式编程,并且学习了几种以引用透明的方式处理某些副作用的方法:
-
国家单子 http://apocalisp.wordpress.com/2011/03/20/towards-an-effect-system-in-scala-part-1对于可变状态,例如更新变量
-
IO 单子 http://james-iry.blogspot.com/2007/11/monads-are-elephants-part-4.html用于 I/O,例如从控制台读取/写入
-
FRP http://flapjax-lang.org用于图形和输入设备事件等交互
但现在大多数“现实世界”的应用程序都与外部系统(如 Web 服务、数据库等)进行交互,这些系统可以由多个用户同时修改,它们具有状态、长时间运行的操作等。所以情况并不那么简单与上述类别一样:向系统询问实体的状态或尝试控制它的结果取决于其状态。此外,交互性也是一个要求:有一些用户可以任意点击的GUI,也许我们还必须自动对来自系统的变化做出反应。
通过最大化纯函数的优势来设计和实现此类应用程序的模式是什么?或者上面的一些方法可以以我没有想到的方式应用于这个问题吗?该语言(例如 Java 或 Scala)并不强制要求 100% 的纯度,因此我对由实践经验支持的实用解决方案感兴趣。
我还没有做过很多这样的事情,但实际上已经以实际的方式完成了,所以希望其他人能够做得更多。不过,我已经用 Scala 编写了一个 Android 应用程序,它满足了您的一些要求; UI 是交互式的,“状态”全部存储在 SQLite 数据库中。数据库和 UI 都需要与 Android 框架交互,而 Android 框架很大程度上是面向 Java 的,因此不太适合 Scala 或函数式编程。
我所做的是采用类似于 MVC 的设计,其中模型部分作为一组仅支持纯操作的 ADT 来实现。这样做的另一个优点是模型代码完全独立于 Android 框架,因此可以在模拟器之外以我喜欢的任何方式进行测试。
这给我留下了控制器(视图是一个非常薄的层,主要只是配置,按照 Android 的工作方式),加上“从数据库加载模型”和“将模型保存到数据库”的附加操作。作为 Scala,我只是用不纯的代码实现了这些部分,这些代码调用纯模型代码来执行实际的数据操作。在 Haskell 中,这些部分可能完全位于 IO monad 中[1]。
总而言之,这使我能够以纯功能的方式思考我的问题领域,而不必在与外部系统交互时“违背常规”。数据库层成为数据库模式和我用于数据模型的 ADT 之间的映射问题;处理这些操作可能失败的事实是控制器(启动数据库操作)的责任,并且不会影响模型。控制器操作在概念上变得非常简单,例如“当按下此按钮时,将当前状态设置为在当前状态上调用函数的结果,然后更新显示表”。最终,这种不纯的“粘合”代码比实际的模型代码要多得多,但我仍然认为以这种方式编写程序是一个胜利,因为我的应用程序的核心是操纵复杂的结构化数据,所以做到这一点是最棘手的。其余的大部分是编写起来很乏味,而不是设计起来很困难。
当您的程序中有大量计算(不一定是大量计算)时,这会起作用。data,只是你实际上在上面计算了东西)。如果该程序几乎完全将不同的外部系统粘合在一起,那么您不一定能获得那么多收益。
[1] 请记住,IO monad 不仅仅用于从控制台读取/写入。其结果受程序外部状态影响的建模操作正是 IO monadfor;一般来说,如果 IO monad 不一直与外部系统交互(或者最好即使是这样),Haskeller 会尽量避免在几乎整个程序中使用 IO monad。您可以对数据进行纯计算以响应来自“外部”的事件,甚至可以进行纯计算of响应外部事件需要执行的 IO 操作(如果很复杂)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)