Представляю вашему вниманию перевод статьи Scott Wlaschin "Designing with types: Making illegal states unrepresentable".
В этой статье мы рассмотрим ключевое преимущество F# — возможность "сделать некорректные состояния невыразимыми" при помощи системы типов (фраза заимствована у Yaron Minsky).
Рассмотрим тип Contact
. В результате проведённого рефакторинга он сильно упростился:
type Contact =
{
Name: Name;
EmailContactInfo: EmailContactInfo;
PostalContactInfo: PostalContactInfo;
}
Теперь предположим, что существует простое бизнес-правило: "Контакт должен содержать адрес электронной почты или почтовый адрес". Соответствует ли наш тип этому правилу?
Нет. Из правила следует, что контакт может содержать адрес электронной почты, но не иметь почтового адреса, или наоборот. Однако в текущем виде тип требует, чтобы были заполнены оба поля.
Кажется, ответ очевиден — сделать адреса необязательными, например, так:
type Contact =
{
Name: PersonalName;
EmailContactInfo: EmailContactInfo option;
PostalContactInfo: PostalContactInfo option;
}
Но теперь наш тип допускает слишком многое. В этой реализации можно создать контакт вообще без адреса, хотя правило требует, чтобы хотя бы один адрес был указан.
Как же решить эту задачу?