数据库和函数式编程是否不一致?


122

我已经从事Web开发已有一段时间了,最​​近开始学习一些函数式编程。像其他人一样,我在将许多这些概念应用于我的专业工作时遇到了很大的麻烦。对我来说,主要原因是我发现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链接
  3. 将我们的函数式语言强制为伪OOP范式,从而消除了真正的函数式编程的某些优雅和稳定性。

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

注意:我个人最熟悉FP方面的LISP,因此,如果您要提供任何示例并了解多种FP语言,则Lisp可能是首选语言

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



4
查看ClojureQL和HaskellDB。它们是利用关系代数的抽象层。
Masse

10
您从错误的前提开始。函数式编程是关于明确和理智地管理状态的全部。实际上,它们与数据库配合得很好。
路西安(Lucian)2012年

3
SQL是最成功的以函数式编程为重点的语言之一,我认为没有固有的困难。
道格拉斯

3
您的#2和#3是错误的二分法。编写原始SQL并不是必须要避免的事情,并且对数据库的抽象不必一定是OOP风格的。
丹·伯顿

Answers:


45

首先,我不会说CLOS(通用Lisp对象系统)是“ pseudo-OO”。这是一流的OO。

第二,我相信您应该使用适合您需求的范例。

当函数是数据流并且确实不需要状态时,就不能无状态地存储数据。

如果您混合了几种需求,请混合使用您的范例。不要限制自己仅使用工具箱的右下角。


3
只是为了好玩,我想我会提到datalog,它试图成为一个更加无状态的数据库。它记录了所有操作,例如“用户喜欢帖子1233”。这些操作将解析为数据库的真实状态。关键在于查询只是事实而不是突变……
Chet

80

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

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

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

我不会尝试编写T-SQL或使用数据库设计概念来创建您的用户界面,为什么您会尝试使用界面语言和设计概念来访问我的数据库?因为您认为SQL还不够好(或新的)?还是您对此感到不舒服?仅仅因为某些事情不适合您觉得最适合的模型,并不意味着它是不好的或错误的。这意味着它是不同的,并且出于合理的原因可能也有所不同。您使用其他工具来完成不同的任务。


“编写SQL的目的是要比任何前端语言更有效地管理前两个注意事项。” 真的吗?那么,为什么是它是SQL的限制还是不能做这样的事情这样
罗宾·格林

但是触发器可以,这是触发器能够处理复杂约束的主要目的之一。
HLGEM

2
我将移动您的最后一段,使其成为主要段落。非常好一点,这与其他人的主张相呼应,这是一种多范式方法(并非一刀切)。
食虫性的,2012年

31

您应该看一下Ben Moseley和Peter Marks的论文“ Out of the Tar Pit”,在这里: “ Out of the Tar Pit”(2006年2月6日)

这是一个现代经典,详述了称为“函数关系编程”的编程范例/系统。尽管与数据库没有直接关系,但它讨论了如何将与外界(例如数据库)的交互与系统的功能核心隔离开来。

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

本文不会给出有关如何集成数据库和函数式编程的确切答案,但是它将帮助您设计一个系统以最大程度地减少问题。


4
多么伟大的论文。您提供的链接已断开(暂时?),但我还在shaffner.us/cs/papers/tarpit.pdf上
38分

2
@queque原始链接仍然无效。我将新链接放入Kevin的答案中。
David Tonhofer 2014年

25
  1. 功能语言的目标不是保持无状态,而是目标是使状态管理变得明确。例如,在Haskell中,您可以将State monad视为“正常”状态的心脏,而IO monad则表示状态的表示,该状态必须存在于程序外部。这两个monad都允许您(a)显式表示有状态的操作,以及(b)通过使用参照透明工具将它们组成来构建有状态的操作。

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

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


15

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

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

我确实认为Lisp中的“ pseudo-oo”想法也有其优点。您可能想尝试一下。如果它们不适合您要使用的数据处理方式,则可以尝试在Weblocks上创建一个图层,该图层可让您以所需的方式使用数据。这可能比自己编写所有内容容易。

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


3
Erlang并不是纯粹的FP,无论该词有什么定义。您通过创建许多进程(全部并行运行)来编写erlang,这些进程相互之间发送消息,就像在smalltalk中的对象一样。因此,从高层的角度来看,它甚至看起来有些面向对象,而且肯定具有状态。如果您将其放大(放大到在进程内部运行的代码),它看起来更有用,但仍不完全是功能,因为您可以从所需的任何位置发送消息(因此也可以进行I / O等),并存储“全局变量”(过程的全局变量,在称为“过程dict”的内部。)
Amadiro,2012年

@Amadiro“肯定有状态”。当然可以。我们总是有状态。问题不是状态,而是可变状态。“功能编程”的一个很好的部分是摆脱脆弱的状态表示(例如,对象实例在我们持有对它们的引用的同时被其他进程更改,以非事务方式增加了对伤害的侮辱)。Erlang具有不可更改的状态,因此符合功能编程的这一标准。因此,任何类型的数据库都不会成为问题。数据库更新是一个问题(另请参见Prolog中的令人讨厌的断言)。
David Tonhofer 2014年

15

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

如果数据库在时间T指出“鲍勃喜欢Suzie”,并且您有一个喜欢数据库的函数接受了数据库和一个类似程序,那么只要您可以在时间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背后的主要思想。


真好 我什至不了解Datomic。另请参阅:Datomic的原理
David Tonhofer 2014年

12

一点也不。有一种类型的数据库称为“功能数据库”,其中,Mnesia也许是最易于访问的示例。基本原理是函数式编程是声明性的,因此可以对其进行优化。您可以使用列表推导实现联接在持久性集合上联接,查询优化器可以自动确定如何实现磁盘访问。

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


1
我认为这不是解决方案。也有面向对象的数据库,但是通常,您希望连接到一个普通的旧式关系SQL数据库。
jalf

4
与OO语言和数据库仍然存在阻抗不匹配的方式,与功能性SQL阻抗不匹配的方式几乎相同。
ConcernedOfTunbridgeWells

1
@ConcernedOfTunbridgeWells我会随意指出,这种阻抗不匹配是用锤子敲打一切需要钉子的人的想象力。非常薄的层和有关SQL的知识可以有效地走很长一段路,因此 jOOQ和类似的垫片。
David Tonhofer 2014年

6

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

对象关系映射器的想法,即您将数据库记录作为对象导入,然后对其进行修改,与面向对象的编程一样适用于功能编程。一个警告是,函数式编程不会修改就位的对象,但是数据库API可以使您修改就位的记录。客户的控制流程如下所示:

  • 将记录导入为对象(此时数据库API可以锁定记录),
  • 根据需要读取对象并根据其内容进行分支,
  • 打包具有所需修改的新对象,
  • 将新对象传递给适当的API调用,该API更新数据库上的记录。

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


5

我对Haskell最满意。最杰出的Haskell Web框架(可与Rails和Django相比)称为Yesod。它似乎有一个非常酷的,类型安全的多后端ORM。看看他们书中的“ 持久性”一章


0

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

例如:

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

               Clojure -> DBMS, Super Foxpro
                   STM -> TransactionMVCC
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:模式和选择

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

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

如果clojure的数据模型像RMDB,clojure的设施像RMDB,clojure的数据操纵像RMDB,clojure必须是RMDB。

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

一切都是RMDB

基于哈希映射(NoSQL)实现关系数据模型和编程

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.