Те, як описана проблема "анемічної моделі", не відповідає успіху FP, як є. По-перше, це потрібно відповідним чином узагальнити. По суті, анемічна модель - це модель, яка містить знання про те, як правильно її використовувати, а не інкапсульована самою моделлю. Натомість ці знання поширюються навколо купи супутніх послуг. Ці служби повинні бути лише клієнтами моделі, але через анемію вони несуть відповідальність за неї. Наприклад, розглянемо Accountклас, який не можна використовувати для активації чи деактивації облікових записів або навіть пошуку інформації про обліковий запис, якщо не обробляється через AccountManagerклас. Обліковий запис повинен відповідати за основні операції над ним, а не якийсь зовнішній клас менеджера.
У функціональному програмуванні подібна проблема існує, коли типи даних не представляють точно те, що вони повинні моделювати. Припустимо, нам потрібно визначити тип, що представляє ідентифікатори користувачів. "Анемічне" визначення зазначає, що ідентифікатори користувачів - це рядки. Це технічно можливо, але виникає величезна проблема, оскільки ідентифікатори користувачів не використовуються як довільні рядки. Немає сенсу об'єднувати їх або вирізати їх підрядки, Unicode насправді не має значення, і вони повинні легко вбудовуватися в URL-адреси та інші контексти із суворими обмеженнями символів та форматів.
Вирішення цієї проблеми зазвичай відбувається в кілька етапів. Простий перший розріз - сказати "Ну, а UserIDпредставлений еквівалентно рядку, але вони різні типи, і ви не можете використовувати одне там, де очікуєте іншого". Haskell (та деякі інші набрані функціональні мови) надають цю функцію через newtype:
newtype UserID = UserID String
Це визначає UserIDфункцію , яка , коли задана Stringбудує значення, яке обробляє , якUserID система типу, але все ще тільки Stringпід час виконання. Тепер функції можуть оголосити, що вони вимагають UserIDзамість рядка; використання UserIDs, де ви раніше використовували захисні рядки проти коду, що об'єднує два UserIDs разом. Система типу гарантує, що цього не може відбутися, ніяких тестів не потрібно.
Слабкість в тому , що код все ще може приймати будь-яке довільне , Stringяк "hello"і побудувати UserIDз нього. Подальші кроки включають створення функції "розумного конструктора", яка при введенні рядка перевіряє деякі інваріанти і повертає лише те, UserIDякщо вони задоволені. Тоді "німий" UserIDконструктор стає приватним, тому, якщо клієнт хоче, UserIDвін повинен використовувати розумний конструктор, тим самим запобігаючи появі неправильно сформованих UserID.
Навіть подальші кроки визначають UserIDтип даних таким чином, що неможливо побудувати той, який неправильно сформований або "неправильний", просто за визначенням. Наприклад, визначення a UserIDяк списку цифр:
data Digit = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine
data UserID = UserID [Digit]
Для побудови UserIDсписку цифр необхідно надати. З огляду на це визначення, неправдиво показати, що UserIDіснування неможливо, яке не може бути представлене в URL-адресі. Визначення подібних моделей даних у Haskell часто допомагає розширеним функціям системи типу, таким як Data Data та General Algebraic Data Types (GADT) , які дозволяють системі типів визначати та доводити більше інваріантів щодо вашого коду. Коли дані відокремлюються від поведінки, визначення даних є єдиним засобом, який потрібно застосовувати до поведінки.