首页 文章

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

提问于
浏览
110

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

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

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

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

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

  • 不要使用功能编程

  • 以恼人的,非抽象的方式访问数据,涉及手动编写大量SQL或类似SQL的代码ala Links

  • 强制我们的功能语言成为伪OOP范例,从而消除了真正的函数式编程的一些优雅和稳定性 .

显然,这些选项似乎都不合理 . 是否找到了绕过这些问题的方法?这里真的有一个问题吗?

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

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

9 回答

  • 12

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

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

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

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

  • 6

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

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

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

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

  • 43

    你应该看一下Ben Moseley和Peter Marks的论文"Out of the Tar Pit",可在这里找到:"Out of the Tar Pit" (Feb. 6, 2006)

    它是一个现代经典,它详细描述了一个名为Functional-Relational Programming的编程范式/系统 . 虽然不直接与数据库相关,但它讨论了如何从系统的功能核心中隔离与外部世界(例如数据库)的交互 .

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

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

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

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

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

  • 4

    我不认为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# .

  • 23

    如果您的数据库不破坏信息,那么您可以通过在整个数据库的函数中作为值工作,以与“纯函数”编程值一致的函数方式使用它 .

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

  • 77

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

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

  • 28

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

  • 14

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

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

    • 将记录作为对象导入(数据库API此时可以锁定记录),

    • 根据需要,根据内容读取对象和分支,

    • 使用所需的修改打包一个新对象,

    • 将新对象传递给相应的API调用,该调用将更新数据库上的记录 .

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

相关问题