An invariant is a rule that must stay true for a domain value to be valid. Instead of repeatedly checking that rule with conditionals, represent valid and unvalidated states as different types. The compiler then prevents code that requires a valid value from receiving an unchecked one. section 8.3.5 section 14.1.1
A single-case discriminated union can wrap a primitive value and give it domain meaning. For example, PhoneNumber and CountryCode can both wrap strings while remaining incompatible types, so a phone number cannot accidentally be supplied where a country code belongs. This removes reliance on comments and catches mix-ups during compilation. section 8.3.5
Validated email as a boundary
Treat an entered email as unvalidated until a validation function constructs a ValidatedEmail. Make email sending accept only ValidatedEmail. Validation is therefore required before sending, while the sending function can stay on its happy path without rechecking the address. section 8.3.5 section 14.1.1
type ValidatedEmail =
private | ValidatedEmail of string #1
member this.Value = match this with (ValidatedEmail e) -> e #2
static member Create (unvalidatedEmail:string) = #3
if unvalidatedEmail.Contains "@" then
Ok (ValidatedEmail unvalidatedEmail)
else
Error "Invalid email!"
let sendEmailSafe message (email:ValidatedEmail) = #4
...
...
...
Ok "Sent message"
let sendResult =
ValidatedEmail.Create "isaac@email.com"
|> Result.bind (sendEmailSafe "Welcome!") #5
A private union case restricts construction of ValidatedEmail to its Create member. Creation returns either a validated value or an error, and Result.bind passes only a successful value to sendEmailSafe. section 14.1.1
Use invariants most effectively by:
- Separating raw input from a value that has passed validation. section 8.3.5
- Making operations accept the validated type rather than checking flags internally. section 14.1.1
- Testing validation at the construction point instead of duplicating invalid-input tests across downstream operations. section 14.1.1











