2008-12-01 41 views
100

我一直是一个Web开发人员,现在已经开始学习一些函数式编程。和其他人一样,我将这些概念中的许多应用于我的专业工作方面遇到了一些重大困难。对我来说,主要原因是我看到FP之间的无状态目标之间的矛盾似乎与我所做的大多数Web开发工作都与数据库密切相关,这些数据库非常以数据为中心。数据库和函数式编程有何不同?

一两件事让我更富有成效开发商对事物的OOP侧像MyGeneration d00dads对于.NET,Class对象关系映射器的发现:: DBI对Perl,ActiveRecord的红宝石,等这使我不用花一天时间写插入和选择语句,并且专注于将数据作为对象轻松处理。当然,我仍然可以在需要它们的权力时编写SQL查询,但否则它会在幕后很好地抽象出来。

现在,转向功能性编程,似乎像许多FP Web框架一样,Links需要编写大量的样板sql代码,如this example。网络锁看起来好一点,但它似乎使用了一种OOP模型来处理数据,并且仍需要为数据库中的每个表手动编写代码,如this example。我想你使用一些代码生成来编写这些映射函数,但是这看起来显然是不喜欢的。 (注意:我没有非常仔细地查看Weblocks或Links,我可能会误解它们是如何使用的)。

所以现在的问题是,对数据库访问的部分(我认为是相当大的)Web应用程序,或其他发展需要的接口与SQL数据库中,我们似乎压低以下路径之一:

  1. 不要使用函数式编程
  2. 访问数据在一个恼人的,非抽象的方式,涉及手动编写大量SQL或类似SQL代码ALA链接
  3. 队我们的功能语言为伪OOP范式,从而消除了真正的函数式编程的一些优雅和稳定性。

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

注:我个人最熟悉的FP前LISP,所以如果你想给任何例子,知道多FP语言,口齿不清很可能是选择的首选语言

PS:具体问题到web开发的其他方面,请参阅this question

+0

另请参阅:http://stackoverflow.com/questions/218190/is-functional-to-relational-mapping-easier-than-object-to-relational – 2008-12-01 09:32:16

+0

@WW - 谢谢,不知何故错过了这个问题。 – 2008-12-01 09:40:46

+4

查看ClojureQL和HaskellDB。它们是利用关系代数的抽象层。 – Masse 2011-08-05 11:34:51

回答

39

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

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

你不能无状态地存储数据,而函数是数据流,并不真正需要状态。

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

10

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

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

13

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

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

我确实认为lisp中的'伪oo'思想也有其优点。你可能想尝试一下。如果它们不适合您想要处理数据的方式,您可以尝试在网络锁之上创建一个图层,以便您按照自己想要的方式处理数据。这可能比自己写一切都容易。

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

71

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

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

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

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

5

我最喜欢Haskell。最突出的Haskell网络框架(相当于Rails和Django)被称为Yesod。它似乎有一个非常酷的,类型安全的多后端ORM。看看他们的书中的Persistance chapter

26

你应该看看纸“走出焦油坑”由本·莫斯利和彼得·马克斯,请访问:"Out of the Tar Pit" (Feb. 6, 2006)

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

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

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

13

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

如果在时间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背后的主要思想。

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

  2. 您引用了许多ORM,根据它们的名称,将抽象数据库作为对象集合。真的,这不是关系数据库中的信息所代表的!根据它的名字,它代表关系数据。 SQL是用于处理关系数据集上的关系的代数(语言),实际上它本身非常“功能化”。 (a)ORM不是映射数据库信息的唯一方法,(b)SQL对于某些数据库设计来说实际上是一种非常好的语言,(c)函数式语言通常具有关系代数映射,它揭示了SQL在惯用语言(以及Haskell,typechecked)时尚方面的威力。

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

2

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

的对象关系映射器,在那里你导入一个数据库记录作为一个对象,然后修改它的想法,只是适用的和有用的函数式编程,因为它是面向对象编程。需要注意的是,功能编程不会修改对象,但数据库API可以允许您修改记录。您的客户端的控制流会是这个样子:

  • 进口记录作为一个对象(数据库API可以锁定在这一点上的记录),
  • 阅读对象和分支基于其内容你喜欢,
  • 打包一个新的对象与您需要的修改,
  • 将新对象传递到适当的API调用更新数据库上的记录。

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