Unit of Measure in practice: [<Measure>] type cm [<Measure>] type ml = cm^3 // a millilitre is 1 cm cubed
To the point in the article about using types to convey meaning, as I commented elsewhere, you want F#s awesome type aliasing and single-case discriminated unions :)
Type aliasing:
type CustomerId = int // We can send now use IDs interchangeably with int, use CustomerId in method signatures, and expand the type with methods
Single Case Discriminated Unions: type CustomerId = CustomerId of int
let id = CustomerId (3) // Have to use a specific constructor
let myFunction (CustomerId(id)) = // DUs can be unpacked in parameters
printfn "%i" id // this function only accepts CustomerId'sA SandwichCount should not be confused with a BurgerCount!
But I will also say this is possible in other languages to some degree with some level of boiler plate.
For example, in java I can do similar. Instead of using float, wrap that in a class called kilometer and use that instead. Then define the api around it as needed (equals, compare, value, etc). Even this small thing I think is really important, and is helpful instead of using primitives or strings. Make email instead of string, dollar instead of just big decimal, etc.
You can eliminate so many confusing bugs just by having better typed primitives.
--- disclaimer: ---
This is not exactly how it works in F# but the languages are too different to really compare