Cara masalah "model anemia" dijelaskan tidak diterjemahkan dengan baik ke FP sebagaimana adanya. Pertama, perlu digeneralisasikan secara tepat. Pada intinya, model anemia adalah model yang berisi pengetahuan tentang cara menggunakannya dengan benar yang tidak dienkapsulasi oleh model itu sendiri. Sebaliknya, pengetahuan itu tersebar di sekitar tumpukan layanan terkait. Layanan-layanan itu seharusnya hanya menjadi klien model, tetapi karena anemia mereka bertanggung jawab untuk itu. Misalnya, pertimbangkan Account
kelas yang tidak dapat digunakan untuk mengaktifkan atau menonaktifkan akun atau bahkan mencari informasi tentang akun kecuali ditangani melalui AccountManager
kelas. Akun tersebut harus bertanggung jawab atas operasi dasar di atasnya, bukan kelas manajer eksternal.
Dalam pemrograman fungsional, masalah serupa terjadi ketika tipe data tidak secara akurat mewakili apa yang seharusnya mereka modelkan. Misalkan kita perlu mendefinisikan tipe yang mewakili ID pengguna. Definisi "anemia" akan menyatakan bahwa ID pengguna adalah string. Itu layak secara teknis, tetapi mengalami masalah besar karena ID pengguna tidak digunakan seperti string arbitrer. Tidak masuk akal untuk menggabungkan mereka atau memotong substring dari mereka, Unicode seharusnya tidak terlalu penting, dan mereka harus mudah disematkan dalam URL dan konteks lain dengan karakter yang ketat dan batasan format.
Memecahkan masalah ini biasanya terjadi dalam beberapa tahap. Potongan pertama yang sederhana adalah untuk mengatakan, "Ya, a UserID
diwakili setara dengan string, tetapi mereka berbeda jenis dan Anda tidak dapat menggunakan yang mana Anda mengharapkan yang lain." Haskell (dan beberapa bahasa fungsional lainnya yang diketik) menyediakan fitur ini melalui newtype
:
newtype UserID = UserID String
Ini mendefinisikan UserID
fungsi yang ketika diberi String
membangun nilai yang diperlakukan seperti sebuah UserID
oleh sistem jenis, tapi yang masih hanya String
pada saat runtime. Sekarang fungsi dapat menyatakan bahwa mereka membutuhkan UserID
bukan string; menggunakan UserID
s di mana Anda sebelumnya menggunakan penjaga string terhadap kode yang menggabungkan dua UserID
s bersama-sama. Jenis sistem menjamin itu tidak bisa terjadi, tidak perlu tes.
Kelemahan di sini adalah bahwa kode masih dapat mengambil String
suka suka "hello"
dan membangun UserID
dari itu. Langkah selanjutnya termasuk membuat fungsi "konstruktor pintar" yang ketika diberi string memeriksa beberapa invarian dan hanya mengembalikan a UserID
jika mereka puas. Kemudian UserID
konstruktor "bodoh" dijadikan pribadi sehingga jika klien menginginkannya, UserID
mereka harus menggunakan konstruktor pintar, sehingga mencegah UserID yang cacat muncul.
Bahkan langkah lebih lanjut mendefinisikan UserID
tipe data sedemikian rupa sehingga tidak mungkin untuk membangun yang salah atau "tidak layak", cukup dengan definisi. Misalnya, mendefinisikan UserID
sebagai daftar digit:
data Digit = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine
data UserID = UserID [Digit]
Untuk membangun UserID
daftar digit harus disediakan. Dengan definisi ini, mudah untuk menunjukkan bahwa tidak mungkin UserID
ada yang tidak dapat direpresentasikan dalam URL. Mendefinisikan model data seperti ini di Haskell sering dibantu oleh fitur sistem tipe canggih seperti Jenis Data dan Generalized Algebraic Data Types (GADTs) , yang memungkinkan sistem tipe untuk menentukan dan membuktikan lebih banyak invarian tentang kode Anda. Ketika data dipisahkan dari perilaku, definisi data Anda adalah satu-satunya cara Anda harus menegakkan perilaku.