Непрозорі типи - це техніка інкапсуляції в Elm.
Дехто також називає це приховуванням інформації, або приховуванням деталей реалізації.
Їх також часто використовують, щоб забезпечити валідацію за підходом Parse, dont validate.
Наприклад, можна мати непрозорий тип EmailAddress, який може містити лише коректні адреси електронної пошти (на відміну від String, який може містити будь-що).
Власні типи мають один або більше варіантів, і компілятор автоматично створює функцію для кожного варіанта, використовуючи назву цього варіанта. Це єдиний спосіб створити власний тип. Компілятор також дозволяє деструктуризацію цих варіантів.
type Maybe a
= Nothing --> Variant implies a 'Nothing' function, with type 'Maybe a', and destructuring of 'Nothing'
| Just a --> Variant implies a 'Just' function, with type 'a -> Maybe a', and destructuring of 'Just _'
Типово все це є внутрішнім для файлу чи модуля, і ми використовуємо ключове слово exposing, щоб вибрати, що експортувати.
Модуль Maybe експортує тип Maybe (щоб інші модулі могли використовувати наявні значення Maybe), а також експортує варіанти Nothing і Just (щоб інші модулі могли створювати значення Maybe і деструктурувати їх).
Для цього він використовує такий синтаксис.
Частина Maybe(..) вказує Elm експортувати всі варіанти, а також визначення власного типу.
module Maybe exposing
( Maybe(..)
-- ...
)
Щоб створити непрозорий тип, досить просто не експортувати варіанти.
Щоб зробити це для типу Maybe, використовують такий синтаксис:
module Maybe exposing
( Maybe
-- ...
)
Коли ми робимо це, майже завжди потрібно надати інший спосіб створювати варіанти та отримувати їх, експортуючи власні функції. Ці власні функції дозволяють застосувати певну логіку під час створення варіантів і отримання їх. Це приховує внутрішні деталі реалізації модуля і означає, що ми можемо змінювати деталі реалізації як завгодно, аби лише експортовані функції залишалися тими самими. Це також дозволяє підтримувати інваріанти під час створення типу та роботи з ним.
Напевно, найпоширеніше застосування непрозорих типів - це забезпечення валідації за підходом Parse, dont validate.
У цьому підході ми експортуємо функцію, яка приймає вхідні дані з меншою структурою чи типобезпекою, ніж потрібно, наприклад String, а потім перетворює їх на структурованіший тип, наприклад EmailAddress, повертаючи або сам тип, або помилку (зазвичай Maybe чи Result).
Наприклад, у коді нижче зовнішні модулі можуть створити тип EmailAddress лише за допомогою функції parseEmailAddress, тож коли ми працюємо з типом EmailAddress, ми впевнені, що він коректний.
module EmailAddress exposing ( EmailAddress, parseEmailAddress, getEmailAddress )
-- The `EmailAddress` custom type has one variant, also called `EmailAddress`
-- When a custom type has one variant, it is idiomatic to use the same name for both.
type EmailAddress =
EmailAddress String
-- create an EmailAddress variant
parseEmailAddress: String -> Maybe EmailAddress
parseEmailAddress candidateEmailAddress =
if isValidEmailAddress candidateEmailAddress then
Just (EmailAddress candidateEmailAddress)
else
Nothing
-- get an EmailAddress variant
getEmailAddress: EmailAddress -> String
getEmailAddress ( EmailAddress emailAdress ) =
emailAdress
isValidEmailAddress: String -> bool
-- ...