データベースと関数型プログラミングは対立していますか?


122

私はしばらくの間Web開発者でしたが、最近、関数型プログラミングの学習を始めました。他の人のように、私はこれらの概念の多くを私の仕事に適用するいくつかの重大な問題を抱えてきました。私にとって、これの主な理由は、FPのステートレスを維持するという目標の間の矛盾が、私が行ったほとんどのWeb開発作業が非常にデータ中心のデータベースに強く結び付けられているという事実とかなり矛盾しているように見えることです。

OOP側でより生産的な開発者になった1つのことは、.GeneのMyGeneration d00dads、PerlのClass :: DBI、RubyのActiveRecordなどのオブジェクトリレーショナルマッパーの発見でした。終日、INSERTステートメントとSELECTステートメントを記述したり、オブジェクトとしてデータを簡単に操作したりすることに集中できます。もちろん、SQLクエリを作成することもできましたが、SQLクエリの能力が必要な場合はそれを実行できましたが、それ以外の場合は裏側でうまく抽象化されました。

さて、関数型プログラミングに移ると、リンクのようなFP Webフレームワークの多くでは、この例のように、ボイラープレートSQLコードを大量に記述する必要があるようです。Weblocksは少し優れているように見えますが、データを操作するために一種のOOPモデルを使用しているようで、この例のように、データベース内のテーブルごとにコードを手動で作成する必要があります。いくつかのコード生成を使用してこれらのマッピング関数を記述していると思いますが、それは明らかにLispに似ていません。

(私はWeblocksやLinksを非常に詳しく見ていないことに注意してください。それらがどのように使用されているのか誤解しているだけかもしれません)。

したがって、問題は、Webアプリケーションのデータベースアクセス部分(かなり大きいと思います)、またはSQLデータベースとのインターフェイスを必要とするその他の開発では、次のいずれかの方法を強制されるようです。

  1. 関数型プログラミングを使用しない
  2. 大量のSQLまたはSQLに似たコードをリンクを手動で作成することを含む、煩わしい、抽象化されていない方法でデータにアクセスする
  3. 関数型言語を強制的に擬似OOPパラダイムにし、真の関数型プログラミングの優雅さと安定性の一部を取り除きます。

明らかに、これらのオプションはどれも理想的ではないようです。これらの問題を回避する方法を見つけましたか?ここに本当に問題がありますか?

注:私は個人的にFPの前にあるLISPに最も精通しているので、例を挙げて複数のFP言語を知りたい場合は、おそらくlispが最適な言語でしょう

PS:Web開発の他の側面に固有の問題については、この質問を参照してください。



4
ClojureQLとHaskellDBを調べてください。これらは、リレーショナル代数を利用した抽象化レイヤーです。
Masse

10
あなたは間違った前提で始めています。関数型プログラミングとは、明示的かつ正常に状態を管理することです。実際、データベースと非常にうまく連携しています。
Lucian

3
SQLは最も機能的なプログラミングに焦点を当てた言語の1つであり、固有の問題はないと思います。
ダグラス

3
あなたの#2と#3は誤った二分法です。生のSQLを書くことは必ずしも避けられるべきものではなく、データベースの抽象化は必ずしもOOP風である必要はありません。
Dan Burton

回答:


45

まず第一に、CLOS(Common Lisp Object System)が「pseudo-OO」であるとは言いません。ファーストクラス○○です。

第二に、あなたはあなたのニーズに合ったパラダイムを使うべきだと思います。

関数はデータの流れであり、実際には状態を必要としませんが、データをステートレスに格納することはできません。

複数のニーズが混在している場合は、パラダイムを組み合わせてください。ツールボックスの右下隅のみを使用するように制限しないでください。


3
ただ面白くするために、よりステートレスなデータベースを目指しているdatalogについて言及したいと思いました。「ユーザーは投稿1233が好き」などのすべてのアクションを記録します。これらのアクションは、データベースの真の状態に解決されます。重要なのは、クエリは突然変異ではなく単なる事実であるということです...
Chet

80

これをデータベース担当者の観点から見ると、フロントエンド開発者は、データベースをモデルに適合させる方法を探すのが、オブジェクト指向または機能的ではなく、リレーショナルであるデータベースの最も効果的な使用方法を検討するのではなく、努力しすぎていることに気付きますセット理論。これにより、一般的にコードのパフォーマンスが低下します。さらに、パフォーマンスの調整が難しいコードを作成します。

データベースアクセスを検討する場合、3つの主な考慮事項があります。データの整合性(すべてのビジネスルールをユーザーインターフェイスを介さずにデータベースレベルで適用する必要がある理由)、パフォーマンス、およびセキュリティです。SQLは、最初の2つの考慮事項をどのフロントエンド言語よりも効果的に管理するように作成されています。それは特にそうするように設計されているからです。データベースのタスクは、ユーザーインターフェイスのタスクとは大きく異なります。タスクの管理に最も効果的なコードのタイプが概念的に異なるのは不思議ではありませんか?

また、データベースは企業の存続に不可欠な情報を保持しています。存続が危機に瀕しているときに、企業が新しい方法を実験する気がないのは不思議です。多くの企業は、既存のデータベースを新しいバージョンにアップグレードすることすら望んでいません。したがって、データベース設計には固有の保守性があります。そして、それは故意にその方法です。

T-SQLを記述したり、データベース設計の概念を使用してユーザーインターフェイスを作成したりしないのですが、なぜデータベースにアクセスするためにインターフェース言語や設計概念を使用しようとするのでしょうか。SQLは十分ではない(または新しい)と思うからですか?それともあなたはそれに慣れていませんか?何かがあなたが最も快適に感じるモデルに合わないからといって、それが悪いか間違っているということではありません。それは、正当な理由により、異なる、そしておそらく異なることを意味します。別のタスクには別のツールを使用します。


「SQLは、最初の2つの考慮事項をどのフロントエンド言語よりも効果的に管理するように作成されています。」まあ、本当に?では、なぜSQL制約はまだこのようなことを実行できないのでしょうか?
ロビングリーン

しかし、トリガーは、複雑な制約を処理できるトリガーの主な目的の1つです。
HLGEM、2012年

2
最後の段落を移動して、先頭の段落にします。非常に良い点は、他の人が求めていることも反映しています。これはマルチパラダイムアプローチです(万能ではありません)。
害虫駆除法

31

:あなたは、紙、こちらをご覧ベン・モーズリーとピーター・マークスによる「タールピットのうち」、見なければならない「タールピットのうち、」(2006年2月6日)

これは、関数型リレーショナルプログラミングと呼ばれるプログラミングパラダイム/システムを詳述する現代の古典です。データベースとは直接関係ありませんが、システムの機能コアから外界(データベースなど)との相互作用を分離する方法について説明します。

また、アプリケーションの内部状態がリレーショナル代数を使用して定義および変更されるシステムを実装する方法についても説明します。これは、明らかにリレーショナルデータベースに関連しています。

このホワイトペーパーでは、データベースと関数型プログラミングを統合する方法について正確な回答はしませんが、問題を最小限に抑えるシステムの設計に役立ちます。


4
なんて素晴らしい紙でしょう。あなたが与えるリンクは(一時的に?)壊れていますが、私はshaffner.us/cs/papers/tarpit.pdf
悲惨な

2
@queque元のリンクはまだ機能していません。新しいリンクをケビンの答えに入れました。
David Tonhofer 14

25
  1. 関数型言語には、ステートレスを維持するという目標はありません。ステートの管理を明示的にするという目標があります。たとえば、Haskellでは、Stateモナドを「通常の」状態の中心と見なすことができ、IOモナドはプログラムの外部に存在しなければならない状態の表現と見なすことができます。これらのモナドの両方を使用すると、(a)ステートフルアクションを明示的に表し、(b)参照透過的なツールを使用してそれらを構成することにより、ステートフルアクションを構築できます。

  2. 複数のORMを参照し、それらの名前ごとに、データベースをオブジェクトのセットとして抽象化します。確かに、これはリレーショナルデータベースの情報が表すものではありません。その名前に基づいて、リレーショナルデータを表します。SQLは、リレーショナルデータセットの関係を処理する代数(言語)であり、実際にはそれ自体が「機能的」です。(a)ORMがデータベース情報をマップする唯一の方法ではないこと、(b)SQLは実際には一部のデータベース設計にとっては非常に優れた言語であること、(c)関数型言語には関係代数があることが多いことを考慮するためにこれを取り上げますSQLの能力を慣用的な(そしてHaskellの場合は型チェックされた)方法で公開するマッピング。

ほとんどのlispsは貧乏人の関数型言語だと思います。それは現代の機能的慣習に従って完全に使用することができますが、それらを必要としないので、コミュニティはそれらを使用する可能性が低くなります。これは、非常に役立つ可能性があるメソッドの混合につながりますが、純粋な関数型インターフェイスがデータベースを有意義に使用する方法を確実に覆い隠します。


15

私はfp言語のステートレスな性質がデータベースへの接続に問題があるとは思いません。Lispは純粋ではない関数型プログラミング言語であるため、状態の処理に問題はありません。そして、Haskellのような純粋な関数型プログラミング言語には、データベースの使用に適用できる入出力を処理する方法があります。

あなたの質問から、あなたの主な問題は、データベースから取得したレコードベースのデータを、大量のSQLを記述せずにlisp-y(lisp-ish?)であるものに抽象化する良い方法を見つけることにあるようですコード。これは、言語パラダイムの問題よりも、ツール/ライブラリの問題のようです。純粋なFPを実行する場合、lispは適切な言語ではない可能性があります。一般的なlispは、純粋なfpよりもoo、fp、およびその他のパラダイムからの優れたアイデアの統合についてのようです。純粋なFPルートを使用する場合は、ErlangまたはHaskellを使用する必要があります。

lispの「pseudo-oo」のアイデアにもメリットがあると思います。あなたはそれらを試してみたいかもしれません。それらがデータを操作する方法に合わない場合は、Weblockの上にレイヤーを作成して、希望どおりにデータを操作できるようにすることができます。これはすべてを自分で書くよりも簡単かもしれません。

免責事項:私はLispの専門家ではありません。私は主にプログラミング言語に興味があり、Lisp / CLOS、Scheme、Erlang、Python、および少しのRubyで遊んでいます。日常のプログラミング生活では、まだC#を使用する必要があります。


3
Erlangは単語の定義によっては純粋なFPではありません。erlangを作成するには、たとえばsmalltalkのオブジェクトのように、互いにメッセージを送信する多数のプロセス(すべて並列で実行)を作成します。したがって、高レベルの観点からは、いくぶんOOっぽく見えるかもしれません。(プロセス内で実行されているコードに)ズームインすると、より機能的に見えますが、必要な場所からメッセージを送信(したがってI / Oなどを実行)したり、保存したりできるため、純粋に機能的ではありません。グローバル変数」(「プロセス
辞書

@Amadiroは「間違いなく状態がある」。もちろんそうです。常に状態があります。問題は状態ではなく、変更可能な状態です。「関数型プログラミング」の良い部分は、もろい状態表現(たとえば、他のプロセスが参照を保持している間に他のプロセスによって変更されたオブジェクトインスタンスを非トランザクション的な方法で傷害に侮辱を加える方法で)を取り除くことです。Erlangは変更不可能な状態であるため、関数型プログラミングのこの基準を満たします。したがって、どのような種類のデータベースでも問題にはなりません。データベースの更新は問題です(Prolog の厄介なアサートも参照)。
David Tonhofer 2014

15

データベースが情報を破壊しない場合は、データベース全体の関数として値を処理することにより、「純粋な関数」プログラミング値と一致する関数的な方法で情報を処理できます。

時間Tでデータベースに「ボブはスージーが好き」と記載されていて、データベースなどを受け入れた関数likeがあった場合、時間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

どういたしまして。「Functional Databases」として知られるデータベースのジャンルがあり、そのでおそらく最もアクセスしやすい例がMnesiaです。基本的な原則は、関数型プログラミングは宣言型であるため、最適化できるということです。永続的なコレクションでリスト内包表記を使用して結合を実装でき、クエリオプティマイザーはディスクアクセスの実装方法を自動的に計算できます。

MnesiaはErlangで記述されており、そのプラットフォームで使用できるWebフレームワーク(Erlyweb)が少なくとも1つあります。Erlangは本質的にシェアードナッシングスレッドモデルと並列であるため、特定の点でスケーラブルアーキテクチャに適しています。


1
それは解決策の多くではないと思います。オブジェクト指向のデータベースもありますが、通常は古いプレーンのリレーショナルSQLデータベースに接続する必要があります。
2008

4
関数型SQLのインピーダンスの不一致があるのとほぼ同じ方法で、オブジェクト指向の言語とデータベースのインピーダンスの不一致があります。
ConcernedOfTunbridgeWells

1
@ConcernedOfTunbridgeWellsこのインピーダンスのミスマッチは、すべてが釘である必要があるハンマーを持つ人々の想像力の産物であると私は恣意的に述べます。非常に薄い層とSQLに関する知識は、エレガントに長い道のりを歩むことができるため、jOOQと同様のシムです。
David Tonhofer 2014

6

データベースは、ステートレスAPIで状態を追跡するのに最適な方法です。RESTをサブスクライブする場合の目標は、クライアントが必要としないように、透過的な方法で状態情報を追跡するデータストア(またはその他のバックエンド)と対話するステートレスコードを記述することです。

データベースレコードをオブジェクトとしてインポートしてから変更するオブジェクトリレーショナルマッパーのアイデアは、関数型プログラミングにもオブジェクト指向プログラミングと同じように適用可能であり便利です。1つの注意点は、関数型プログラミングではオブジェクトを適切に変更しないことですが、データベース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:Schema-and-select

私が提唱しているのは、ハッシュマップの上にリレーショナルデータモデルを構築して、NoSQLとRMDBの利点を組み合わせることです。これは実際にはposgtresqlの逆実装です。

アヒルのタイピング:アヒルのように見え、アヒルのように鳴く場合、それはアヒルでなければなりません。

RMDBのようなclojureのデータモデル、RMDBのようなclojureの機能、およびRMDBのようなclojureのデータ操作の場合、clojureはRMDBでなければなりません。

Clojureは、リレーショナルデータベース理論に基づく関数型プログラミング言語です。

すべてがRMDBです

ハッシュマップ(NoSQL)に基づくリレーショナルデータモデルとプログラミングの実装

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.