数据库和函数式编程有何不同?

我已经有了一段时间的Web开发人员,并且最近开始学习一些函数式编程。 和其他人一样,我将这些概念中的很多应用到我的专业工作上,都遇到了很大的麻烦。 对我来说,主要原因是我看到FP之间的剩余无状态目标之间的冲突似乎与我所做的大多数Web开发工作已严重依赖于数据库,这是非常以数据为中心的事实相冲突。

有一件事让我成为OOP方面更有成效的开发者,就是发现了MyGeneration d00dads for .NET,Class :: DBI for perl,ActiveRecord for ruby​​等对象关系映射器。这使我可以远离从写插入和select语句整天,并专注于作为对象容易地处理数据。 当然,我们仍然可以在需要权限时编写SQL查询,但是在幕后很好地抽象出来。

现在,转向function性编程,就像在这个例子中 ,像Links这样的许多FP Web框架似乎需要编写大量的样板化sql代码。 networking锁看起来好一点,但它好像使用了一种OOP模型来处理数据,并且仍然需要为数据库中的每个表手动编写代码,如本例中所示 。 我想你使用了一些代码生成来编写这些映射函数,但是这看起来好像是一个不假思索的样子。

(请注意,我没有非常仔细地查看Weblocks或Links,我可能会误解它们是如何使用的)。

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

  1. 不要使用函数式编程
  2. 以恼人的,不抽象的方式访问数据,涉及手动编写大量SQL或类似SQL的代码ala链接
  3. 强制我们的function性语言变成伪OOP范例,从而消除了真正的函数式编程的一些优雅和稳定性。

显然,这些select都不是理想的。 是否find了解决这些问题的方法? 这里真的有一个问题吗?

注意:我个人最熟悉FP前端的LISP,所以如果你想给出任何例子并且知道多种FP语言,lisp可能是首选的语言select

PS:有关Web开发其他方面的问题,请参阅此问题 。

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

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

你不能无状态地存储数据,而一个函数是数据stream,并不需要状态。

如果你有几个混合的需求,混合你的范例。 不要只限于使用工具箱的右下angular。

从数据库人员的angular度来看,我发现前端开发人员试图想方设法使数据库适合他们的模型,而不是考虑最有效的方法来使用数据库,而不是面向对象或function,但关系和使用设置理论。 我看到这通常导致代码执行不良。 并进一步创build难以调整性能的代码。

在考虑数据库访问时,有三个主要考虑因素 – 数据完整性(为什么所有的业务规则都应该在数据库级别而不是通过用户界面执行),性能和安全性。 编写SQL是为了比前端语言更有效地pipe理前两个问题。 因为它是专门devise来做到这一点。 数据库的任务远远不同于用户界面的任务。 难怪在pipe理任务中最有效的代码types在概念上是不同的?

数据库拥有对公司生存至关重要的信息。 难怪企业在生存受到威胁时不愿尝试新方法。 很多企业甚至不愿升级到现有数据库的新版本。 所以在数据库devise中有固有的保守性。 这是故意的。

我不会尝试编写T-SQL或使用数据库devise概念来创build用户界面,为什么要尝试使用界面语言和devise概念来访问我的数据库? 因为你认为SQL不够花哨(或新的)? 或者你觉得不舒服? 只是因为某些东西不适合你最舒适的模式,并不意味着它是不好的或错误的。 这意味着,这是不同的,可能是有正当理由的不同。 您使用不同的工具来完成不同的任务。

你可以看看Ben Moseley和Peter Marks的论文“走出焦油坑”,可以在这里find: “走出焦油坑”(2006年2月6日)

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

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

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

  1. function语言没有保持无状态的目标,他们的目标是使状态pipe理更加明确。 例如,在Haskell中,可以将状态monad视为“正常”状态的核心,并将IO monad视为程序外部必须存在的状态表示。 这两个monad都允许你:(a)明确地表示有状态的动作;(b)通过使用引用透明的工具来构build有状态的动作。

  2. 您引用了许多ORM,根据它们的名称,将抽象数据库作为对象集合。 诚然,这不是关系数据库中的信息所代表的! 根据它的名字,它代表关系数据。 SQL是处理关系数据集上的关系的代数(语言),实际上它本身就是“function性”的。 (a)ORM不是映射数据库信息的唯一方法,(b)SQL对于某些数据库devise来说实际上是一个非常好的语言,(c)函数式语言通常具有关系代数映射,这暴露了SQL在惯用语言(和Haskell的情况下,typechecked)时尚的力量。

我会说大多数lisp是一个穷人的function语言。 它完全有能力按照现代function实践使用,但是由于它不需要它们,所以社区不太可能使用它们。 这会导致混合使用非常有用的方法,但肯定会掩盖纯粹的function接口仍然可以有效地使用数据库。

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

从你的问题看来,你的主要问题在于find一种很好的方法,把你从数据库中得到的基于logging的数据抽象成lisp-y(lisp-ish?),而不必编写大量的SQL码。 这似乎更像是一个工具/库的问题,而不是语言范例的问题。 如果你想做纯FP,也许lisp不适合你。 Common Lisp似乎更多的是关于整合oo,fp和其他范式的好点子,而不是关于纯fp。 也许你应该使用Erlang或Haskell,如果你想要去纯FP路线。

我确实认为Lisp中的“伪oo”思想也有其优点。 你可能想尝试一下。 如果它们不适合您想要处理数据的方式,则可以尝试在Weblock上创build一个图层,使您可以按照自己想要的方式处理数据。 这可能比自己写一切都容易。

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

如果你的数据库没有破坏信息,那么你可以通过在整个数据库的函数中作为一个值工作,以符合“纯function性”编程值的function方式使用它。

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

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

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

我最喜欢Haskell。 最突出的Haskellnetworking框架(相当于Rails和Django)被称为Yesod。 它似乎有一个很酷,types安全,多后端的ORM。 看看他们书中的Persistant章节 。

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

一个对象关系映射器的概念,将数据库logging作为一个对象导入,然后对其进行修改,对于函数式编程来说,就像面向对象编程一样。 有一点需要注意的是函数式编程不会修改对象 ,但数据库API可以让您修改logging 。 你的客户端的控制stream程如下所示:

  • 将logging导入为对象(此时数据库API可以lockinglogging),
  • 根据你喜欢的内容读取对象和分支,
  • 打包一个新的对象与您所需的修改,
  • 将新对象传递给更新数据库logging的相应API调用。

数据库将更新您的更改logging。 纯粹的函数式编程可能会禁止在您的程序范围内重新分配variables,但您的数据库API仍然可以允许就地更新。