我已经做了一段时间的Web开发人员,并且最近开始学习一些函数式编程.像其他人一样,我在将这些概念应用于我的专业工作时遇到了一些重大问题.对我来说,主要原因是我看到FP之间保持无状态的目标之间的冲突似乎与我所做的大多数Web开发工作都与数据库密切相关的事实相矛盾,数据库是以数据为中心的.
有一件事,让我更富有成效开发商对事物的OOP侧像MyGeneration d00dads对于.NET,Class对象关系映射器的发现:: DBI对Perl,ActiveRecord的红宝石等,这让我走就走从整天编写插入和选择语句,并专注于作为对象轻松处理数据.当然,我仍然可以在需要它们的时候编写SQL查询,但是否则它在幕后很好地抽象出来.
现在,转向功能编程,好像有很多喜欢的链接FP Web框架的要求写了很多的样板SQL代码,在这个例子中.Weblocks似乎好一点,但它似乎用一种OOP的模型与数据的工作,并且仍然需要代码手动为每个表在数据库中写入这个例子.我想你使用一些代码生成来编写这些映射函数,但这似乎绝对不像lisp.
(注意我没有仔细查看Weblocks或Links,我可能只是误解了它们的使用方式).
所以问题是,对于Web应用程序的数据库访问部分(我认为相当大),或者需要与sql数据库接口的其他开发,我们似乎被迫关闭以下路径之一:
不要使用功能编程
以恼人的,非抽象的方式访问数据,涉及手动编写大量SQL或类似SQL的代码链接
强制我们的功能语言成为伪OOP范例,从而消除了真正的函数式编程的一些优雅和稳定性.
显然,这些选项似乎都不合理.是否找到了绕过这些问题的方法?这里真的有一个问题吗?
注意:我个人最熟悉FP前端的LISP,所以如果您想提供任何示例并了解多种FP语言,那么lisp可能是首选语言
PS:对于Web开发其他方面的特定问题,请参阅此问题.
从数据库人员的角度来看,我发现前端开发人员太过努力找到使数据库适合他们模型的方法,而不是考虑使用非面向对象或功能但关系和使用数据库的最有效方法设置理论.我已经看到这通常导致代码性能不佳.而且它还会创建难以进行性能调优的代码.
在考虑数据库访问时,有三个主要考虑因素 - 数据完整性(为什么所有业务规则应在数据库级别而不是通过用户界面强制实施),性能和安全性.编写SQL是为了比任何前端语言更有效地管理前两个注意事项.因为它是专门为此而设计的.数据库的任务与用户界面的任务大不相同.难怪在管理任务时最有效的代码类型在概念上是不同的吗?
数据库拥有对公司生存至关重要的信息.难怪企业在生存受到威胁时不愿意尝试新方法.许多企业甚至不愿意升级到现有数据库的新版本.因此,数据库设计存在固有的保守性.这是刻意的.
我不会尝试编写T-SQL或使用数据库设计概念来创建用户界面,为什么要尝试使用界面语言和设计概念来访问我的数据库?因为您认为SQL不够花哨(或新)吗?或者你觉得不舒服?仅仅因为某些东西不适合你觉得最舒服的模型,并不意味着它是坏的或错的.这意味着它是不同的,并且可能由于正当理由而不同.您可以使用其他工具执行其他任务.
首先,我不会说CLOS(Common Lisp Object System)是"伪OO".这是一流的OO.
其次,我相信你应该使用符合你需求的范例.
您不能无状态地存储数据,而功能是数据流并且实际上不需要状态.
如果您有多种需求混合,请混合您的范例.不要仅限于使用工具箱的右下角.
你应该看看本·莫斯利和彼得·马克斯的"走出焦油坑"这篇论文:"走出焦油坑"(2006年2月6日)
它是一个现代经典,它详细描述了一个名为Functional-Relational Programming的编程范式/系统.虽然不直接与数据库相关,但它讨论了如何从系统的功能核心中隔离与外部世界(例如数据库)的交互.
本文还讨论了如何实现,其中应用程序的内部状态被定义和修改使用关系代数,这显然关系到关系数据库的系统.
本文不会给出如何集成数据库和函数式编程的确切答案,但它将帮助您设计一个系统来最小化问题.
功能语言没有保持无状态的目标,它们的目标是使状态管理明确.例如,在Haskell中,您可以将State monad视为"正常"状态的核心,IO monad是状态的表示,它必须存在于程序之外.这两个monad都允许您(a)明确表示有状态操作,(b)通过使用引用透明工具组合它们来构建有状态操作.
您引用了许多ORM,这些ORM根据其名称将抽象数据库作为对象集.实际上,这不是关系数据库中的信息所代表的!根据其名称,它代表关系数据.SQL是用于处理关系数据集上的关系的代数(语言),实际上它本身就非常"功能".我提出这一点,以便考虑(a)ORM不是映射数据库信息的唯一方法,(b)SQL对于某些数据库设计来说实际上是一种非常好的语言,(c)函数式语言通常具有关系代数映射以一种惯用语(以及Haskell,typechecked)的方式暴露SQL的强大功能.
我会说大多数lisps是一个穷人的功能语言.它完全能够根据现代功能实践使用,但由于它不需要它们,社区不太可能使用它们.这导致了各种方法的混合,这些方法非常有用,但肯定会模糊纯粹的功能接口如何仍然有意义地使用数据库.
我不认为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#.
如果您的数据库不破坏信息,那么您可以通过在整个数据库的函数中作为值工作,以与"纯函数"编程值一致的函数方式使用它.
如果在时间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背后的主要思想.
一点也不.有一种被称为"功能数据库"的数据库,其中Mnesia可能是最容易获得的例子.基本原则是函数式编程是声明性的,因此可以进行优化.您可以使用持久集合上的List Comprehensions实现连接,并且查询优化器可以自动计算如何实现磁盘访问.
Mnesia是用Erlang编写的,至少有一个Web框架(Erlyweb)可用于该平台.Erlang本质上与无共享线程模型并行,因此在某些方面它适用于可扩展的体系结构.
我最熟悉的是Haskell.最着名的Haskell Web框架(与Rails和Django相当)称为Yesod.它似乎有一个非常酷,类型安全,多后端ORM.看看他们书中的Persistance章节.
数据库是在无状态API中跟踪状态的理想方法。如果您订阅REST,那么您的目标是编写与数据存储区(或其他后端)交互的无状态代码,该数据存储以透明的方式跟踪状态信息,从而使您的客户端不必这样做。
对象关系映射器的思想,即您将数据库记录作为对象导入,然后对其进行修改,与面向对象的编程一样适用于函数式编程,并且非常有用。一个警告是,函数式编程不会修改就位的对象,但是数据库API可以允许您修改就位的记录。客户的控制流程如下所示:
将记录导入为一个对象(数据库API此时可以锁定记录),
根据需要读取对象并根据其内容进行分支,
打包具有所需修改的新对象,
将新对象传递给适当的API调用,该API更新数据库上的记录。
数据库将使用您的更改来更新记录。纯函数式编程可能不允许在程序范围内重新分配变量,但数据库API仍可以就地更新。