130

我从事网络开发已经有一段时间了,最​​近开始学习一些函数式编程。像其他人一样,我在将这些概念中的许多概念应用于我的专业工作时遇到了一些重大麻烦。对我来说,造成这种情况的主要原因是我看到 FP 保持无状态的目标之间的冲突似乎与我所做的大多数 Web 开发工作都与非常以数据为中心的数据库密切相关这一事实相矛盾。

让我成为 OOP 方面更高效的开发人员的一件事是发现了对象关系映射器,例如 .Net 的 MyGeneration d00dads、perl 的 Class::DBI、ruby 的 ActiveRecord 等。这让我远离从整天编写插入和选择语句,到专注于将数据作为对象轻松处理。当然,当需要 SQL 查询的功能时,我仍然可以编写它们,但除此之外,它在幕后被很好地抽象出来了。

现在,转向函数式编程,似乎许多 FP Web 框架(如 Links)需要编写大量样板 sql 代码,如本例所示。Weblocks 似乎好一点,但它似乎使用一种 OOP 模型来处理数据,并且仍然需要为数据库中的每个表手动编写代码,如本例所示。我想您使用一些代码生成来编写这些映射函数,但这似乎绝对不像lisp。

(请注意,我没有非常仔细地研究过 Weblocks 或 Links,我可能只是误解了它们的使用方式)。

所以问题是,对于 Web 应用程序的数据库访问部分(我相信它相当大),或其他需要与 sql 数据库接口的开发,我们似乎被迫走以下路径之一:

  1. 不要使用函数式编程
  2. 以令人讨厌的、非抽象的方式访问数据,包括手动编写大量 SQL 或类似 SQL 的代码 ala Links
  3. 强制我们的函数式语言进入伪 OOP 范式,从而消除真正函数式编程的一些优雅和稳定性。

显然,这些选项似乎都不理想。是否找到了规避这些问题的方法?这里真的有一个问题吗?

注意:我个人最熟悉 FP 方面的 LISP,所以如果你想给出任何例子并了解多种 FP 语言,lisp 可能是首选语言

PS:对于特定于 Web 开发其他方面的问题,请参阅此问题

4

10 回答 10

83

从数据库人员的角度来看,我发现前端开发人员过于努力地寻找使数据库适合他们的模型的方法,而不是考虑使用不是面向对象或功能但关系和使用数据库的最有效方法集合论。我已经看到这通常会导致代码性能不佳。此外,它还创建了难以调整性能的代码。

在考虑数据库访问时,有三个主要考虑因素 - 数据完整性(为什么所有业务规则都应该在数据库级别而不是通过用户界面强制执行)、性能和安全性。编写 SQL 是为了比任何前端语言更有效地管理前两个注意事项。因为它是专门为此设计的。数据库的任务与用户界面的任务大不相同。难怪在管理任务中最有效的代码类型在概念上是不同的?

数据库保存着对公司生存至关重要的信息。难怪企业在生存受到威胁时不愿意尝试新方法。哎呀,许多企业甚至不愿意升级到现有数据库的新版本。因此,数据库设计中存在固有的保守主义。它是故意的。

我不会尝试编写 T-SQL 或使用数据库设计概念来创建您的用户界面,为什么您会尝试使用您的界面语言和设计概念来访问我的数据库?因为您认为 SQL 不够花哨(或新)?还是你觉得不舒服?仅仅因为某些东西不适合您最喜欢的模型,并不意味着它是坏的或错误的。这意味着它是不同的,并且可能出于正当的原因而不同。您为不同的任务使用不同的工具。

于 2008-12-01T19:22:28.380 回答
46

首先,我不会说 CLOS(Common Lisp Object System)是“伪 OO”。这是一流的OO。

其次,我认为你应该使用适合你需要的范式。

您不能无状态地存储数据,而函数是数据流并且不需要状态。

如果你有几个混合的需求,混合你的范式。不要限制自己只使用工具箱的右下角。

于 2008-12-01T11:33:00.763 回答
31

您应该查看 Ben Moseley 和 Peter Marks 的论文“Out of the Tar Pit”,可在此处获得:“Out of the Tar Pit”(2006 年 2 月 6 日)

这是一部现代经典,详细介绍了称为函数式关系编程的编程范式/系统。虽然与数据库没有直接关系,但它讨论了如何将与外部世界(例如数据库)的交互与系统的功能核心隔离开来。

本文还讨论了如何实现一个系统,其中使用关系代数定义和修改应用程序的内部状态,这显然与关系数据库有关。

本文不会给出如何集成数据库和函数式编程的确切答案,但会帮助您设计一个系统以最小化问题。

于 2012-05-08T10:01:00.597 回答
26
  1. 函数式语言的目标不是保持无状态,它们的目标是使状态管理变得明确。例如,在 Haskell 中,您可以将 State monad 视为“正常”状态的核心,而 IO monad 是必须存在于程序之外的状态表示。这两个 monad 都允许您 (a) 显式表示有状态的操作,以及 (b) 通过使用引用透明工具组合它们来构建有状态的操作。

  2. 您引用了许多 ORM,根据它们的名称,它们将数据库抽象为对象集。确实,这不是关系数据库中的信息所代表的!顾名思义,它代表关系数据。SQL 是一种用于处理关系数据集上的关系的代数(语言),实际上它本身就非常“实用”。我提出这一点是为了考虑(a)ORM 不是映射数据库信息的唯一方法,(b)SQL 实际上对于某些数据库设计来说是一种非常好的语言,以及(c)函数式语言通常具有关系代数映射以惯用的方式(在 Haskell 的情况下,类型检查)中暴露 SQL 的力量。

我会说大多数 lisps 是穷人的功能语言。它完全可以根据现代功能实践使用,但由于它不需要它们,因此社区不太可能使用它们。这导致混合的方法可能非常有用,但肯定会掩盖纯函数式接口如何仍然有意义地使用数据库。

于 2012-05-08T13:32:58.623 回答
15

我不认为 fp 语言的无状态特性是连接到数据库的问题。Lisp 是一种非纯函数式编程语言,因此处理状态应该没有任何问题。像 Haskell 这样的纯函数式编程语言有处理输入和输出的方法,可以应用于使用数据库。

从您的问题看来,您的主要问题似乎在于找到一种好方法,将您从数据库中返回的基于记录的数据抽象为 lisp-y(lisp-ish?)而无需编写大量 SQL代码。这似乎更像是工具/库的问题,而不是语言范例的问题。如果您想做纯 FP,那么 lisp 可能不适合您。Common lisp 似乎更多的是整合来自 oo、fp 和其他范例的好想法,而不是纯粹的 fp。如果你想走纯 FP 路线,也许你应该使用 Erlang 或 Haskell。

我确实认为 lisp 中的“伪 oo”想法也有其优点。您可能想尝试一下。如果它们不适合您处理数据的方式,您可以尝试在 Weblocks 之上创建一个层,让您可以按照自己的方式处理数据。这可能比自己编写所有内容更容易。

免责声明:我不是 Lisp 专家。我对编程语言最感兴趣,并且一直在使用 Lisp/CLOS、Scheme、Erlang、Python 和一点 Ruby。在日常的编程生活中,我仍然被迫使用 C#。

于 2008-12-01T10:44:54.103 回答
15

如果您的数据库不破坏信息,那么您可以通过将整个数据库的功能作为一个值工作,以与“纯功能”编程值一致的功能方式使用它。

如果在时间 T 数据库声明“Bob 喜欢 Suzie”,并且您有一个接受数据库和喜欢者的函数 likes,那么只要您可以在时间 T 恢复数据库,您就有一个涉及数据库的纯函数程序. 例如

# Start: Time T
likes(db, "Bob")
=> "Suzie"
# Change who bob likes
...
likes(db "Bob")
=> "Alice"
# Recover the database from T
db = getDb(T)
likes(db, "Bob")
=> "Suzie"

为此,您永远不能丢弃您可能使用的信息(这实际上意味着您不能丢弃信息),因此您的存储需求将单调增加。但是您可以将数据库作为一系列线性离散值开始工作,其中后续值通过事务与先前的值相关。

例如,这是Datomic背后的主要思想。

于 2012-05-08T12:20:17.267 回答
12

一点也不。有一种类型的数据库被称为“功能数据库”,其中Mnesia可能是最容易获得的例子。基本原理是函数式编程是声明式的,所以可以优化。您可以在持久集合上使用List Comprehensions实现连接,查询优化器可以自动计算出如何实现磁盘访问。

Mnesia 是用Erlang编写的,并且至少有一个 Web 框架 ( Erlyweb ) 可用于该平台。Erlang 本质上与无共享线程模型并行,因此在某些方面它适用于可扩展架构。

于 2008-12-01T09:33:41.657 回答
6

数据库是在无状态 API 中跟踪状态的完美方式。如果您订阅 REST,那么您的目标是编写与数据存储(或其他一些后端)交互的无状态代码,以透明的方式跟踪状态信息,这样您的客户端就不必这样做了。

Object-Relational Mapper 的想法,将数据库记录作为对象导入然后修改它,对于函数式编程和面向对象编程一样适用和有用。需要注意的是,函数式编程不会就地修改对象,但数据库 API 可以允许您就地修改记录。您的客户端的控制流程如下所示:

  • 将记录作为对象导入(此时数据库API可以锁定记录),
  • 根据你喜欢的内容读取对象和分支,
  • 用您想要的修改打包一个新对象,
  • 将新对象传递给相应的 API 调用,该调用会更新数据库上的记录。

数据库将使用您的更改更新记录。纯函数式编程可能不允许在程序范围内重新分配变量,但您的数据库 API 仍然允许就地更新。

于 2015-08-28T23:01:25.147 回答
5

我对 Haskell 最满意。最著名的 Haskell Web 框架(可与 Rails 和 Django 相媲美)称为 Yesod。它似乎有一个很酷的、类型安全的、多后端的 ORM。看看他们书中的持久性章节

于 2012-05-08T09:21:05.060 回答
1

数据库和函数式编程可以融合。

例如:

Clojure 是一种基于关系数据库理论的函数式编程语言。

               Clojure -> DBMS, Super Foxpro
                   STM -> Transaction,MVCC
Persistent Collections -> db, table, col
              hash-map -> indexed data
                 Watch -> trigger, log
                  Spec -> constraint
              Core API -> SQL, Built-in function
              function -> Stored Procedure
             Meta Data -> System Table

注意:在最新的 spec2 中,spec 更像是 RMDB。请参阅:spec-alpha2 wiki:模式和选择

我提倡:在hash-map之上构建关系数据模型,实现NoSQL和RMDB优势的结合。这实际上是 posgtresql 的反向实现。

Duck Typing:如果它看起来像鸭子,叫起来像鸭子,那它一定是鸭子。

如果 clojure 的数据模型像 RMDB,clojure 的工具像 RMDB,clojure 的数据操作像 RMDB,那么 clojure 必须是 RMDB。

Clojure 是一种基于关系数据库理论的函数式编程语言

一切都是 RMDB

基于hash-map(NoSQL)实现关系数据模型和编程

于 2019-05-11T13:58:04.467 回答