Sind Datenbanken und funktionale Programmierung uneins?

122

Ich bin seit einiger Zeit Webentwickler und habe vor kurzem angefangen, funktionale Programmierung zu lernen. Wie andere hatte ich einige erhebliche Probleme, viele dieser Konzepte auf meine berufliche Arbeit anzuwenden. Für mich ist der Hauptgrund dafür, dass ein Konflikt zwischen dem Ziel von FP, staatenlos zu bleiben, ziemlich im Widerspruch zu der Tatsache steht, dass die meisten meiner Webentwicklungsarbeiten stark an Datenbanken gebunden waren, die sehr datenorientiert sind.

Eine Sache, die mich auf der OOP-Seite zu einem viel produktiveren Entwickler machte, war die Entdeckung objektrelationaler Mapper wie MyGeneration d00dads für .Net, Class :: DBI für Perl, ActiveRecord für Ruby usw. Dadurch konnte ich mich fernhalten vom Schreiben von Insert- und Select-Anweisungen den ganzen Tag über bis hin zum einfachen Arbeiten mit den Daten als Objekte. Natürlich konnte ich immer noch SQL-Abfragen schreiben, wenn ihre Leistung benötigt wurde, aber ansonsten wurde sie hinter den Kulissen schön abstrahiert.

Wenn wir uns nun der funktionalen Programmierung zuwenden, scheint es, als müssten bei vielen FP-Webframeworks wie Links wie in diesem Beispiel viel SQL-Code auf der Boilerplate geschrieben werden . Weblocks scheint ein wenig besser zu sein, aber es scheint eine Art OOP-Modell für die Arbeit mit Daten zu verwenden, und es muss weiterhin wie in diesem Beispiel für jede Tabelle in Ihrer Datenbank manuell Code geschrieben werden . Ich nehme an, Sie verwenden eine Codegenerierung, um diese Zuordnungsfunktionen zu schreiben, aber das scheint entschieden un-lisp-artig zu sein.

(Hinweis: Ich habe Weblocks oder Links nicht besonders genau betrachtet. Ich verstehe möglicherweise nur falsch, wie sie verwendet werden.)

Die Frage ist also, dass wir für die Datenbankzugriffsteile (von denen ich glaube, dass sie ziemlich groß sind) der Webanwendung oder für andere Entwicklungen, die eine Schnittstelle mit einer SQL-Datenbank erfordern, gezwungen sind, einen der folgenden Pfade einzuschlagen:

  1. Verwenden Sie keine funktionale Programmierung
  2. Greifen Sie auf lästige, nicht abstrahierte Weise auf Daten zu, indem Sie viel SQL oder SQL-ähnlichen Code ala Links manuell schreiben
  3. Erzwingen Sie unsere funktionale Sprache in ein Pseudo-OOP-Paradigma und entfernen Sie so einen Teil der Eleganz und Stabilität einer echten funktionalen Programmierung.

Natürlich scheint keine dieser Optionen ideal zu sein. Hat ein Weg gefunden, diese Probleme zu umgehen? Gibt es hier wirklich überhaupt ein Problem?

Hinweis: Ich persönlich kenne LISP im FP-Bereich am besten. Wenn Sie also Beispiele nennen und mehrere FP-Sprachen kennen möchten, ist Lisp wahrscheinlich die bevorzugte Sprache

PS: Zu Problemen, die für andere Aspekte der Webentwicklung spezifisch sind, siehe diese Frage .

Tristan Havelick
quelle
4
Schauen Sie sich ClojureQL und HaskellDB an. Sie sind Abstraktionsschichten, die relationale Algebra verwenden.
Masse
10
Sie beginnen mit der falschen Prämisse. Bei der funktionalen Programmierung geht es darum, den Zustand explizit und vernünftig zu verwalten. Sie arbeiten tatsächlich sehr gut mit Datenbanken.
Lucian
3
SQL ist eine der erfolgreichsten Sprachen für die funktionale Programmierung. Ich glaube nicht, dass es irgendwelche inhärenten Schwierigkeiten gibt.
Douglas
3
Ihre Nr. 2 und Nr. 3 sind eine falsche Zweiteilung. Das Schreiben von unformatiertem SQL sollte nicht unbedingt vermieden werden, und Abstraktionen über eine Datenbank müssen nicht unbedingt OOP-artig sein.
Dan Burton

Antworten:

45

Zunächst würde ich nicht sagen, dass CLOS (Common Lisp Object System) "Pseudo-OO" ist. Es ist erstklassig OO.

Zweitens glaube ich, dass Sie das Paradigma verwenden sollten, das Ihren Bedürfnissen entspricht.

Sie können Daten nicht zustandslos speichern, während eine Funktion ein Datenfluss ist und keinen Status benötigt.

Wenn Sie mehrere Bedürfnisse vermischt haben, mischen Sie Ihre Paradigmen. Beschränken Sie sich nicht darauf, nur die untere rechte Ecke Ihrer Toolbox zu verwenden.

Svante
quelle
3
Nur zum Spaß dachte ich, ich würde das Datenprotokoll erwähnen, das versucht, eine zustandslosere Datenbank zu sein. Es zeichnet alle Aktionen auf, z. B. "Benutzer mag Post 1233." Diese Aktionen werden in den wahren Status der Datenbank aufgelöst. Der Schlüssel ist, dass Abfragen eher Fakten als Mutationen sind ...
Chet
80

Wenn ich dies aus der Sicht einer Datenbankperson betrachte, finde ich, dass Front-End-Entwickler sich zu sehr bemühen, Wege zu finden, um Datenbanken an ihr Modell anzupassen, anstatt die effektivsten Wege zur Verwendung von Datenbanken in Betracht zu ziehen, die nicht objektorientiert oder funktional, sondern relational und nutzbar sind Mengenlehre. Ich habe gesehen, dass dies im Allgemeinen zu einer schlechten Leistung des Codes führt. Außerdem wird Code erstellt, der nur schwer auf die Leistung abgestimmt werden kann.

Bei der Betrachtung des Datenbankzugriffs gibt es drei Hauptaspekte: Datenintegrität (warum alle Geschäftsregeln auf Datenbankebene und nicht über die Benutzeroberfläche durchgesetzt werden sollten), Leistung und Sicherheit. SQL wurde geschrieben, um die ersten beiden Überlegungen effektiver zu verwalten als jede Front-End-Sprache. Weil es speziell dafür entwickelt wurde. Die Aufgabe einer Datenbank unterscheidet sich stark von der Aufgabe einer Benutzeroberfläche. Ist es ein Wunder, dass die Art von Code, die bei der Verwaltung der Aufgabe am effektivsten ist, konzeptionell unterschiedlich ist?

Und Datenbanken enthalten Informationen, die für das Überleben eines Unternehmens entscheidend sind. Es ist kein Wunder, dass Unternehmen nicht bereit sind, mit neuen Methoden zu experimentieren, wenn ihr Überleben auf dem Spiel steht. Viele Unternehmen sind nicht einmal bereit, auf neue Versionen ihrer vorhandenen Datenbank zu aktualisieren. Das Datenbankdesign weist also einen inhärenten Konservatismus auf. Und das ist bewusst so.

Ich würde nicht versuchen, T-SQL zu schreiben oder Datenbankdesignkonzepte zum Erstellen Ihrer Benutzeroberfläche zu verwenden. Warum sollten Sie versuchen, Ihre Schnittstellensprache und Designkonzepte für den Zugriff auf meine Datenbank zu verwenden? Weil Sie denken, SQL ist nicht schick (oder neu) genug? Oder fühlen Sie sich damit nicht wohl? Nur weil etwas nicht zu dem Modell passt, mit dem Sie sich am wohlsten fühlen, heißt das nicht, dass es schlecht oder falsch ist. Es bedeutet, dass es anders ist und wahrscheinlich aus einem legitimen Grund anders. Sie verwenden ein anderes Werkzeug für eine andere Aufgabe.

HLGEM
quelle
"SQL wurde geschrieben, um die ersten beiden Überlegungen effektiver zu verwalten als jede Front-End-Sprache." Ja wirklich? Warum, dann ist es , dass SQL - Constraints noch nicht Dinge wie tut dies ?
Robin Green
Aber ein Trigger kann, das ist einer der Hauptzwecke von Triggern, um komplexe Einschränkungen bewältigen zu können.
HLGEM
2
Ich würde Ihren letzten Absatz so verschieben, dass er der führende Absatz ist. Sehr guter Punkt, der das wiedergibt, was andere ebenfalls fordern, was ein Multi-Paradigma-Ansatz ist (kein One-Size-Fits-All).
Pestophagous
31

Sie sollten sich die Zeitung "Out of the Tar Pit" von Ben Moseley und Peter Marks ansehen, die hier erhältlich ist: "Out of the Tar Pit" (6. Februar 2006)

Es ist ein moderner Klassiker, der ein Programmierparadigma / -system namens Functional-Relational Programming beschreibt. Obwohl es sich nicht direkt um Datenbanken handelt, wird erläutert, wie Interaktionen mit der Außenwelt (z. B. Datenbanken) vom Funktionskern eines Systems isoliert werden können.

In diesem Artikel wird auch erläutert, wie ein System implementiert wird, bei dem der interne Status der Anwendung mithilfe einer relationalen Algebra definiert und geändert wird, die offensichtlich mit relationalen Datenbanken zusammenhängt.

Dieses Dokument gibt keine genaue Antwort auf die Integration von Datenbanken und funktionaler Programmierung, hilft Ihnen jedoch beim Entwurf eines Systems zur Minimierung des Problems.

Kevin Albrecht
quelle
4
Was für ein tolles Papier. Der Link, den Sie geben, ist defekt (vorübergehend?), Aber ich habe das Papier auch unter shaffner.us/cs/papers/tarpit.pdf
pestophagous
2
@queque Der ursprüngliche Link ist noch tot. Ich habe den neuen Link in Kevins Antwort eingefügt.
David Tonhofer
25
  1. Funktionale Sprachen haben nicht das Ziel, staatenlos zu bleiben, sie haben das Ziel, die Verwaltung des Staates explizit zu machen. In Haskell können Sie beispielsweise die Staatsmonade als das Herz des "normalen" Zustands und die E / A-Monade als eine Darstellung des Zustands betrachten, der außerhalb des Programms existieren muss. Mit diesen beiden Monaden können Sie (a) zustandsbehaftete Aktionen explizit darstellen und (b) zustandsbehaftete Aktionen erstellen, indem Sie sie mit referenziell transparenten Werkzeugen zusammenstellen.

  2. Sie verweisen auf eine Reihe von ORMs, die gemäß ihrem Namen abstrakte Datenbanken als Objektgruppen darstellen. Dies ist wirklich nicht das, was die Informationen in einer relationalen Datenbank darstellen! Gemäß seinem Namen repräsentiert es relationale Daten. SQL ist eine Algebra (Sprache) zum Behandeln von Beziehungen in einem relationalen Datensatz und ist selbst ziemlich "funktional". Ich spreche dies an, um zu berücksichtigen, dass (a) ORMs nicht die einzige Möglichkeit sind, Datenbankinformationen abzubilden, (b) dass SQL für einige Datenbankdesigns tatsächlich eine ziemlich nette Sprache ist und (c) dass funktionale Sprachen häufig eine relationale Algebra haben Zuordnungen, die die Leistungsfähigkeit von SQL auf idiomatische (und im Fall von Haskell typgeprüfte) Weise offenlegen.

Ich würde sagen, die meisten Lisps sind die funktionale Sprache eines armen Mannes. Es ist in der Lage, gemäß modernen funktionalen Praktiken verwendet zu werden, aber da es sie nicht erfordert, ist es weniger wahrscheinlich, dass die Community sie verwendet. Dies führt zu einer Mischung von Methoden, die sehr nützlich sein können, aber sicherlich verdecken, wie reine funktionale Schnittstellen Datenbanken noch sinnvoll nutzen können.

J. Abrahamson
quelle
15

Ich denke nicht, dass die Staatenlosigkeit von fp-Sprachen ein Problem beim Herstellen einer Verbindung zu Datenbanken ist. Lisp ist eine nicht reine funktionale Programmiersprache, daher sollte es keine Probleme mit dem Status geben. Und reine funktionale Programmiersprachen wie Haskell haben Möglichkeiten, mit Ein- und Ausgaben umzugehen, die auf die Verwendung von Datenbanken angewendet werden können.

Aus Ihrer Frage geht hervor, dass Ihr Hauptproblem darin besteht, einen guten Weg zu finden, um die auf Datensätzen basierenden Daten, die Sie aus Ihrer Datenbank zurückerhalten, in etwas zu abstrahieren, das lisp-y (lisp-ish?) Ist, ohne viel SQL schreiben zu müssen Code. Dies scheint eher ein Problem mit den Werkzeugen / Bibliotheken als ein Problem mit dem Sprachparadigma zu sein. Wenn Sie reine FP machen möchten, ist Lisp möglicherweise nicht die richtige Sprache für Sie. Common Lisp scheint mehr mit der Integration guter Ideen aus oo, fp und anderen Paradigmen zu tun zu haben als mit reinem fp. Vielleicht sollten Sie Erlang oder Haskell verwenden, wenn Sie den reinen FP-Weg gehen möchten.

Ich denke, die 'Pseudo-Oo'-Ideen in Lisp haben auch ihren Wert. Vielleicht möchten Sie sie ausprobieren. Wenn sie nicht der Art und Weise entsprechen, wie Sie mit Ihren Daten arbeiten möchten, können Sie versuchen, über Weblocks eine Ebene zu erstellen, mit der Sie mit Ihren Daten nach Ihren Wünschen arbeiten können. Dies ist möglicherweise einfacher, als alles selbst zu schreiben.

Haftungsausschluss: Ich bin kein Lisp-Experte. Ich interessiere mich hauptsächlich für Programmiersprachen und habe mit Lisp / CLOS, Scheme, Erlang, Python und ein bisschen Ruby gespielt. Im täglichen Programmierleben bin ich immer noch gezwungen, C # zu verwenden.

Mendelt
quelle
3
Erlang ist nach keiner Definition des Wortes eine reine FP. Sie schreiben erlang, indem Sie viele Prozesse erstellen (alle laufen parallel), die Nachrichten aneinander senden, ähnlich wie Objekte in beispielsweise Smalltalk. Aus einer hochrangigen Perspektive mag es sogar etwas OO-artig erscheinen und hat definitiv einen Zustand. Wenn Sie hineinzoomen (in den Code hinein, der in einem Prozess ausgeführt wird), sieht er funktionaler aus, ist aber immer noch nicht rein funktional, da Sie Nachrichten (und damit E / A usw.) von jedem gewünschten Ort aus senden und speichern können. " globale Variablen "(global für den Prozess, innerhalb von etwas, das als" Prozessdiktat "bezeichnet wird.)
Amadiro
@ Amadiro "hat definitiv Zustand". Natürlich tut es das. Wir haben immer Staat. Das Problem ist nicht Zustand, es ist veränderlicher Zustand . Ein guter Teil der "funktionalen Programmierung" besteht darin, spröde Zustandsdarstellungen zu beseitigen (z. B. Objektinstanzen, die durch andere Prozesse geändert werden, während wir auf sie verweisen, und zwar auf nicht transaktionale Weise, um die Verletzung zu beleidigen). Erlang hat einen nicht veränderlichen Zustand und erfüllt somit diese Kriterien der funktionalen Programmierung. Und damit: Datenbanken jeglicher Art sind nie ein Problem. Datenbank - Updates ist ein Problem (siehe auch die bösen behauptet in Prolog).
David Tonhofer
15

Wenn Ihre Datenbank keine Informationen zerstört, können Sie auf funktionale Weise mit ihnen arbeiten, die mit "rein funktionalen" Programmierwerten übereinstimmen, indem Sie Funktionen der gesamten Datenbank als Wert bearbeiten.

Wenn zum Zeitpunkt T in der Datenbank angegeben ist, dass "Bob Suzie mag", und Sie eine Funktion hatten, die eine Datenbank und einen Liker akzeptiert, haben Sie ein reines Funktionsprogramm, das eine Datenbank umfasst, solange Sie die Datenbank zum Zeitpunkt T wiederherstellen können . z.B

# 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"

Um dies zu tun, können Sie niemals Informationen wegwerfen, die Sie möglicherweise verwenden (was praktisch bedeutet, dass Sie keine Informationen wegwerfen können), sodass Ihr Speicherbedarf monoton ansteigt. Sie können jedoch beginnen, mit Ihrer Datenbank als lineare Reihe diskreter Werte zu arbeiten, wobei nachfolgende Werte durch Transaktionen mit den vorherigen Werten in Beziehung gesetzt werden.

Dies ist beispielsweise die Hauptidee von Datomic .

Tier
quelle
Nett. Ich wusste nicht einmal über Datomic. Siehe auch: Begründung für Datomic .
David Tonhofer
12

Überhaupt nicht. Es gibt ein Genre von Datenbanken, die als "funktionale Datenbanken" bekannt sind Mnesia vielleicht das am besten zugängliche Beispiel ist. Das Grundprinzip ist, dass die funktionale Programmierung deklarativ ist, damit sie optimiert werden kann. Sie können einen Join mithilfe von Listenverständnissen implementieren für persistente Sammlungen und der Abfrageoptimierer kann automatisch herausfinden, wie der Festplattenzugriff implementiert wird.

Mnesia ist in Erlang geschrieben und es gibt mindestens ein Webframework ( Erlyweb ) für diese Plattform. Erlang ist von Natur aus parallel zu einem Threading-Modell ohne gemeinsame Nutzung und eignet sich daher in gewisser Weise für skalierbare Architekturen.

ConcernedOfTunbridgeWells
quelle
1
Ich denke nicht, dass das eine gute Lösung ist. Es gibt auch objektorientierte Datenbanken, aber normalerweise möchten Sie eine Verbindung zu einer einfachen alten relationalen SQL-Datenbank herstellen.
Jalf
4
Sie haben immer noch eine Impedanzfehlanpassung mit OO-Sprachen und -Datenbanken, ähnlich wie Sie eine funktionale SQL-Impedanzfehlanpassung haben.
ConcernedOfTunbridgeWells
1
@ConcernedOfTunbridgeWells Ich werde willkürlich feststellen, dass diese Impedanzfehlanpassung eine Erfindung der Vorstellungskraft von Menschen mit Hämmern ist, die alles brauchen, um Nägel zu sein. Sehr dünne Schichten und Kenntnisse über SQL können elegant einen langen Weg gehen, daher jOOQ und ähnliche Shims.
David Tonhofer
6

Eine Datenbank ist der perfekte Weg, um den Status in einer zustandslosen API zu verfolgen. Wenn Sie REST abonnieren, besteht Ihr Ziel darin, zustandslosen Code zu schreiben, der mit einem Datenspeicher (oder einem anderen Backend) interagiert, der die Statusinformationen auf transparente Weise verfolgt, damit Ihr Client dies nicht muss.

Die Idee eines objektrelationalen Mappers, bei dem Sie einen Datenbankeintrag als Objekt importieren und dann ändern, ist für die funktionale Programmierung ebenso anwendbar und nützlich wie für die objektorientierte Programmierung. Die einzige Einschränkung besteht darin, dass durch die funktionale Programmierung das vorhandene Objekt nicht geändert wird. Mit der Datenbank-API können Sie jedoch den vorhandenen Datensatz ändern . Der Kontrollfluss Ihres Kunden würde ungefähr so ​​aussehen:

  • Importieren Sie den Datensatz als Objekt (die Datenbank-API kann den Datensatz an dieser Stelle sperren).
  • Lesen Sie das Objekt und den Zweig anhand seines Inhalts nach Ihren Wünschen.
  • Verpacken Sie ein neues Objekt mit den gewünschten Änderungen.
  • Übergeben Sie das neue Objekt an den entsprechenden API-Aufruf, der den Datensatz in der Datenbank aktualisiert.

Die Datenbank aktualisiert den Datensatz mit Ihren Änderungen. Eine reine funktionale Programmierung erlaubt möglicherweise keine Neuzuweisung von Variablen im Rahmen Ihres Programms , aber Ihre Datenbank-API kann weiterhin direkte Aktualisierungen zulassen.

Gebratene Brice
quelle
5

Ich fühle mich mit Haskell am wohlsten. Das bekannteste Haskell-Webframework (vergleichbar mit Rails und Django) heißt Yesod. Es scheint ein ziemlich cooles, typsicheres Multi-Backend-ORM zu haben. Schauen Sie sich das Kapitel Ausdauer in ihrem Buch an.

Honza Pokorny
quelle
0

Datenbanken und funktionale Programmierung können zusammengeführt werden.

beispielsweise:

Clojure ist eine funktionale Programmiersprache, die auf der Theorie relationaler Datenbanken basiert.

               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

Hinweis: In der neuesten Spezifikation2 ähnelt die Spezifikation eher RMDB. sehen: spec-alpha2 wiki: Schema-and-select

Ich befürworte: Erstellen eines relationalen Datenmodells auf der Grundlage einer Hash-Map, um eine Kombination aus NoSQL- und RMDB-Vorteilen zu erzielen. Dies ist eigentlich eine umgekehrte Implementierung von posgtresql.

Ententypisierung: Wenn es wie eine Ente aussieht und wie eine Ente quakt, muss es eine Ente sein.

Wenn das Datenmodell von clojure wie eine RMDB, die Funktionen von clojure wie eine RMDB und die Datenmanipulation von clojure wie eine RMDB ist, muss clojure eine RMDB sein.

Clojure ist eine funktionale Programmiersprache, die auf der Theorie relationaler Datenbanken basiert

Alles ist RMDB

Implementieren Sie ein relationales Datenmodell und eine Programmierung basierend auf Hash-Map (NoSQL).

Lin Pengcheng
quelle