Sposób, w jaki opisany jest problem „modelu anemicznego”, nie przekłada się dobrze na FP. Najpierw należy go odpowiednio uogólnić. Sercem modelu anemicznego jest model, który zawiera wiedzę o tym, jak właściwie go używać, który nie jest zamknięty w samym modelu. Zamiast tego wiedza ta rozprzestrzenia się na stos powiązanych usług. Usługi te powinny być tylko klientami tego modelu, ale z powodu anemii ponoszą za to odpowiedzialność . Rozważmy na przykład Accountklasę, której nie można użyć do aktywacji lub dezaktywacji kont, a nawet wyszukiwania informacji o koncie, chyba że są obsługiwane przez AccountManagerklasę. Konto powinno być odpowiedzialne za podstawowe operacje na nim, a nie niektóre zewnętrzne klasy menedżerów.
W programowaniu funkcjonalnym podobny problem występuje, gdy typy danych nie odzwierciedlają dokładnie tego, co powinny modelować. Załóżmy, że musimy zdefiniować typ reprezentujący identyfikatory użytkownika. Definicja „anemiczna” oznaczałaby, że identyfikatory użytkowników są łańcuchami. Jest to technicznie wykonalne, ale napotyka ogromne problemy, ponieważ identyfikatory użytkowników nie są używane jak ciągi arbitralne. Nie ma sensu ich łączenia ani wycinania podciągów, Unicode nie powinien mieć większego znaczenia i powinny być łatwe do osadzenia w adresach URL i innych kontekstach o ściśle ograniczonych znakach i formatach.
Rozwiązanie tego problemu zwykle przebiega w kilku etapach. Prostym pierwszym cięciem jest powiedzenie: „Cóż, a UserIDjest reprezentowane równorzędnie z łańcuchem, ale są to różne typy i nie można użyć jednego tam, gdzie oczekujesz drugiego”. Haskell (i niektóre inne wpisane języki funkcjonalne) udostępnia tę funkcję poprzez newtype:
newtype UserID = UserID String
To definiuje UserIDfunkcję, która gdy dali Stringkonstruuje wartość, która jest traktowana jakUserID przez system typu, ale jest nadal tylko Stringprzy starcie. Teraz funkcje mogą zadeklarować, że wymagają UserIDzamiast ciągu znaków; używając UserIDs, gdzie wcześniej używałeś łańcuchów chroniących przed kodem UserIDłączącym dwa s razem. System typów gwarantuje, że to się nie stanie, nie są wymagane żadne testy.
Słabością jest to, że kod może nadal brać dowolnej Stringjak "hello"i zbudować UserIDz niego. Dalsze kroki obejmują utworzenie „inteligentnego konstruktora”, który po podaniu łańcucha sprawdza niektóre niezmienniki i zwraca tylko, UserIDjeśli są spełnione. Następnie „głupi” UserIDkonstruktor zostaje ustawiony na prywatny, więc jeśli klient chce UserID, musi użyć inteligentnego konstruktora, zapobiegając w ten sposób powstaniu zniekształconych identyfikatorów użytkowników.
Nawet dalsze kroki definiują UserIDtyp danych w taki sposób, że niemożliwe jest zbudowanie takiego, który jest zniekształcony lub „niewłaściwy”, po prostu z definicji. Na przykład zdefiniowanie UserIDjako listy cyfr:
data Digit = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine
data UserID = UserID [Digit]
Aby zbudować UserIDlistę cyfr, należy podać. Biorąc pod uwagę tę definicję, trywialne jest pokazanie, że UserIDistnienie, którego nie można przedstawić w adresie URL, jest niemożliwe . Definiowanie modeli danych, jak to w Haskell jest często wspomagane przez zaawansowanych funkcji systemowych, takich jak typ Rodzaje danych i typy uogólnione dane (GADTs algebraiczne) , które pozwalają na system typu zdefiniować i udowodnić więcej niezmienników o kodzie. Gdy dane są oddzielone od zachowania, definicja danych jest jedynym sposobem wymuszenia zachowania.