我还没有做很多这种实际上以实际方式完成的事情,所以其他人希望能够做得更多。然而,我已经用 Scala 编写了一个 Android 应用程序,它有你的几个要求;UI 是交互式的,“状态”全部存储在 SQLite 数据库中。数据库和 UI 都需要与 Android 框架交互,这些框架非常面向 Java,因此不太适合 Scala 或函数式编程。
我所做的是采用类似于 MVC 设计的东西,其中模型部分被实现为一组仅支持纯操作的 ADT。这还有一个额外的优势,即模型代码完全独立于 Android 框架,因此可以在模拟器之外以我喜欢的任何方式对其进行测试。
这给我留下了控制器(视图是一个非常薄的层,主要是配置,Android 的工作方式),加上“从数据库加载模型”和“将模型保存到数据库”的附加操作。作为 Scala,我只是用不纯代码实现了这些部分,这些代码调用纯模型代码来进行实际的数据操作。在 Haskell 中,这些部分可能完全在 IO monad[1] 中。
总而言之,这让我能够以纯粹的功能性术语来思考我的问题域,而不必在与外部系统交互时“违背常规”。数据库层变成了数据库模式和我用于数据模型的 ADT 之间的映射问题;处理这些操作可能失败的事实是控制器(启动数据库操作)的责任,并且不会影响模型。控制器操作在概念上变得非常简单,例如“按下此按钮时,将当前状态设置为在当前状态上调用函数的结果,然后更新显示表”。最后,这种不纯的“胶水”代码比实际的模型代码要多得多,但我仍然认为以这种方式编写程序是一种胜利,因为我的应用程序的核心是处理复杂的结构化数据,所以要做到这一点是最棘手的。其余的主要是编写乏味而不是难以设计。
当您的程序中有大量计算(不一定是大量数据,只是您实际在其上进行计算)时,这将起作用。如果该程序几乎完全将不同的外部系统粘合在一起,那么您不一定有那么多收获。
[1] 请记住,IO monad 不仅仅用于从控制台读取/写入。结果受程序外部状态影响的建模操作正是 IO monad 的用途;通常,如果 IO monad 不是一直与外部系统交互(或者最好即使是),Haskellers 会尽量避免在几乎整个程序中使用 IO monad。您可以对数据进行纯计算以响应来自“外部”的事件,或者甚至对需要执行以响应外部事件的 IO 操作进行纯计算,如果这很复杂的话。