Die Art und Weise, wie das Problem des "anämischen Modells" beschrieben wird, lässt sich nicht so gut auf FP übertragen, wie es ist. Zunächst muss es entsprechend verallgemeinert werden. Im Kern ist ein anämisches Modell ein Modell, das Wissen über die ordnungsgemäße Verwendung enthält, das nicht vom Modell selbst eingekapselt wird. Stattdessen wird dieses Wissen auf einen Stapel verwandter Dienste verteilt. Diese Dienste sollten nur Kunden des Modells sein, aber aufgrund ihrer Anämie werden sie dafür verantwortlich gemacht. Angenommen, eine AccountKlasse kann nicht zum Aktivieren oder Deaktivieren von Konten oder sogar zum Nachschlagen von Informationen zu einem Konto verwendet werden, es sei denn, sie wird über eine AccountManagerKlasse verarbeitet. Das Konto sollte für grundlegende Vorgänge verantwortlich sein, nicht für eine externe Manager-Klasse.
Bei der funktionalen Programmierung besteht ein ähnliches Problem, wenn Datentypen nicht genau das darstellen, was sie modellieren sollen. Angenommen, wir müssen einen Typ definieren, der Benutzer-IDs darstellt. Eine "anämische" Definition würde angeben, dass Benutzer-IDs Zeichenfolgen sind. Das ist technisch machbar, stößt aber auf große Probleme, da Benutzer-IDs nicht wie willkürliche Zeichenfolgen verwendet werden. Es macht keinen Sinn, sie zu verketten oder Teilzeichenfolgen daraus herauszuschneiden. Unicode sollte eigentlich keine Rolle spielen und sie sollten leicht in URLs und andere Kontexte mit strengen Zeichen- und Formatbeschränkungen eingebettet werden können.
Die Lösung dieses Problems erfolgt normalerweise in wenigen Schritten. Ein einfacher erster Schnitt lautet: "Nun, a UserIDwird gleichbedeutend mit einer Zeichenfolge dargestellt, aber es handelt sich um verschiedene Typen, und Sie können keinen verwenden, bei dem Sie den anderen erwarten." Haskell (und einige andere typisierte funktionale Sprachen) bietet diese Funktion über newtype:
newtype UserID = UserID String
Dies definiert eine UserIDFunktion, die bei Angabe eines StringKonstrukts einen Wert erzeugt, der vom Typsystem wie ein behandeltUserID wird, der aber nur Stringzur Laufzeit vorliegt. Jetzt können Funktionen deklarieren, dass sie einen UserIDanstelle eines Strings benötigen . Verwenden von UserIDs, bei denen Sie zuvor Zeichenfolgen verwendet haben, um zu verhindern, dass Code zwei UserIDs zusammenfügt. Das Typensystem garantiert, dass dies nicht passieren kann, es sind keine Tests erforderlich.
Die Schwäche dabei ist, dass Code immer noch ein beliebiges StringLike nehmen "hello"und daraus ein konstruieren UserIDkann. Weitere Schritte umfassen das Erstellen einer "Smart-Konstruktor" -Funktion, die bei Angabe eines Strings einige Invarianten überprüft und nur dann ein zurückgibt, UserIDwenn sie zufrieden sind. Dann wird der "dumme" UserIDKonstruktor privat gemacht. Wenn ein Client dies wünscht UserID, muss er den intelligenten Konstruktor verwenden, wodurch verhindert wird, dass fehlerhafte Benutzer-IDs entstehen.
In weiteren Schritten wird der UserIDDatentyp so definiert , dass es nicht möglich ist , einen fehlerhaften oder "unpassenden" Datentyp zu erstellen . So definieren Sie beispielsweise eine UserIDals Liste von Ziffern:
data Digit = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine
data UserID = UserID [Digit]
Zum Erstellen einer UserIDListe müssen Ziffern angegeben werden. In Anbetracht dieser Definition ist es trivial zu zeigen, dass es unmöglich ist UserID, dass ein Element existiert, das nicht in einer URL dargestellt werden kann. Das Definieren solcher Datenmodelle in Haskell wird häufig durch erweiterte Typsystemfunktionen wie Datentypen und Generalized Algebraic Data Types (GADTs) unterstützt , mit denen das Typsystem mehr Invarianten für Ihren Code definieren und nachweisen kann. Wenn Daten vom Verhalten entkoppelt sind, müssen Sie das Verhalten nur durch Ihre Datendefinition erzwingen.