数据库和功能编程是否存在争议?

时间:2008-12-01 09:28:20

标签: sql database functional-programming lisp

我已经做了一段时间的Web开发人员,并且最近开始学习一些函数式编程。像其他人一样,我在将这些概念应用于我的专业工作时遇到了一些重大问题。对我来说,主要原因是我发现FP之间保持无状态的目标之间的冲突似乎与我所做的大多数Web开发工作都与数据库密切相关的事实相矛盾,数据库是以数据为中心的。

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

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

(注意我没有仔细查看Weblocks或Links,我可能只是误解了它们的使用方式)。

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

  1. 不使用功能编程
  2. 以恼人的,非抽象的方式访问数据,涉及手动编写大量SQL或类似SQL的代码ala Links
  3. 强制我们的功能语言成为伪OOP范例,从而消除了真正的函数式编程的一些优雅和稳定性。
  4. 显然,这些选项似乎都不合理。是否找到了绕过这些问题的方法?这里真的有一个问题吗?

    注意:我个人最熟悉FP前端的LISP,所以如果您想提供任何示例并了解多种FP语言,那么lisp可能是首选语言

    PS:有关Web开发其他方面的特定问题,请参阅this question

10 个答案:

答案 0 :(得分:78)

从数据库人的角度出发,我发现前端开发人员很难找到使数据库适合他们的模型的方法,而不是考虑使用非面向对象或功能的数据库的最有效方法。关系和使用集合论。我已经看到这通常导致代码性能不佳。而且它还会创建难以进行性能调优的代码。

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

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

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

答案 1 :(得分:44)

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

其次,我相信你应该使用符合你需要的范例。

您无法无状态地存储数据,而功能是数据流并且实际上不需要状态。

如果您需要混合多种需求,请混合您的范例。不要仅限于使用工具箱的右下角。

答案 2 :(得分:29)

你应该看一下Ben Moseley和Peter Marks的文章“Out of the Tar Pit”,可在此处找到:"Out of the Tar Pit" (Feb. 6, 2006)

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

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

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

答案 3 :(得分:23)

  1. 功能语言没有保持无状态的目标,他们的目标是明确管理状态。例如,在Haskell中,您可以将State monad视为“正常”状态的核心,IO monad是状态的表示,它必须存在于程序之外。这两个monad都允许您(a)明确表示有状态操作,(b)通过使用引用透明工具组合它们来构建有状态操作。

  2. 您引用了许多ORM,这些ORM根据其名称将抽象数据库作为对象集。实际上,这不是关系数据库中的信息所代表的!根据其名称,它代表关系数据。 SQL是用于处理关系数据集上的关系的代数(语言),实际上它本身就非常“功能”。我提出这一点,以便考虑(a)ORM不是映射数据库信息的唯一方法,(b)SQL对于某些数据库设计来说实际上是一种非常好的语言,(c)函数式语言通常具有关系代数映射以一种惯用语(以及Haskell,typechecked)的方式暴露SQL的强大功能。

  3. 我想说大多数的lisps是一个穷人的功能语言。它完全能够根据现代功能实践使用,但由于它不需要它们,社区不太可能使用它们。这导致了各种方法的混合,这些方法非常有用,但肯定会模糊纯粹的功能接口如何仍然有意义地使用数据库。

答案 4 :(得分:14)

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

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

我确实认为lisp中的'伪oo'想法也有其优点。你可能想尝试一下。如果它们不符合您希望使用数据的方式,您可以尝试在Weblocks上创建一个层,以便您可以按照自己的方式处理数据。这可能比自己写一切容易。

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

答案 5 :(得分:14)

如果您的数据库没有销毁信息,那么您可以通过在整个数据库的函数中作为值来以与“纯函数”编程值一致的函数方式使用它。

如果在时间T,数据库声明“Bob喜欢Suzie”,并且您有一个喜欢接受数据库和liker的功能,那么只要您可以在时间T恢复数据库,您就拥有一个纯粹的功能程序涉及一个数据库。 e.g。

# 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背后的主要理念,例如。

答案 6 :(得分:12)

完全没有。有一种称为“功能数据库”的数据库,其中Mnesia可能是最容易访问的例子。基本原则是函数式编程是声明性的,因此可以进行优化。您可以在持久集合上使用List Comprehensions实现连接,并且查询优化器可以自动计算如何实现磁盘访问。

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

答案 7 :(得分:6)

我对Haskell最满意。最着名的Haskell Web框架(与Rails和Django相当)称为Yesod。它似乎有一个非常酷,类型安全,多后端ORM。在他们的书中查看Persistance chapter

答案 8 :(得分:6)

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

对象关系映射器的概念,您将数据库记录作为对象导入然后对其进行修改,对于函数式编程和面向对象编程同样适用且有用。需要注意的是,函数式编程不会修改对象,但数据库API可以允许您修改记录。客户端的控制流程如下所示:

  • 将记录导入为对象(此时数据库API可以锁定记录),
  • 根据需要,根据内容阅读对象和分支,
  • 使用所需的修改打包新对象
  • 将新对象传递给相应的API调用,该调用将更新数据库中的记录。

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

答案 9 :(得分:0)

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

例如:

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

注意:在最新规范2中,规范更像RMDB。 参见:spec-alpha2 wiki: Schema-and-select

我主张:在哈希映射之上构建关系数据模型,以实现NoSQL和RMDB的优点的结合。这实际上是posgtresql的反向实现。

鸭子打字:如果它看起来像鸭子,而象鸭子一样嘎嘎叫,那一定是鸭子。

如果clojure的数据模型(如RMDB),clojure的设施(如RMDB)和clojure的数据操纵(如RMDB),则clojure必须是RMDB。

Clojure is a functional programming language based on relational database theory

Everything is RMDB

Implement relational data model and programming based on hash-map (NoSQL)