Skip to content

Instantly share code, notes, and snippets.

@Savelenko
Last active September 8, 2020 23:56
Show Gist options
  • Save Savelenko/ee690d15d5712fad2b16d15480e7e97e to your computer and use it in GitHub Desktop.
Save Savelenko/ee690d15d5712fad2b16d15480e7e97e to your computer and use it in GitHub Desktop.
Phantom types with private representation
namespace PhantomTypes
module User =
type Username<'a> = private Username of string with
member u.Value = let (Username username) = u in username
type Short = interface end
type Full = interface end
// Make a username suitable for authentication where a short username is required.
let authenticationUsername name : Username<Short> =
// Imagine validation here + Result in the signature
Username name
// Refine the phantom type variable, i.e. provide some guarantee. In this case that the resulting username contains
// the Full name, regardless of input.
let fullName database (username : Username<'a>) : Username<Full> =
// Find all user data in the DB. If 'username' was already Full, then nothing to do. Otherwise retrieve the full
// name from the specific column somewhere.
let (Username name) = username
let fullName = (failwith "Find full name in the DB") name database
Username fullName
module ClientModule =
open User
// Not allowed
// let root = Username "root"
// This is the only way a username can be made and it is forced to be Short. Module User could provide more smart
// constructor functions.
let root = User.authenticationUsername "root" // Username<Short>
// This functions does not care about Short/Long. The fact that we can have functions like this and functions which
// require a specific "format" at the same time is what makes this all useful; see below.
let isLongerThanFive (username : Username<'a>) = 5 < username.Value.Length
// A greeting needs to include a Full name.
let greeting (username : Username<Full>) =
sprintf "Dear %s" username.Value // We have a guarantee here
// Combine
let greetingToRoot db =
let rootFull = root |> User.fullName db // Username<Full>
if isLongerThanFive rootFull then "Too much trouble" else greeting rootFull
// Does not type-check, must go through f-n fullName
// if isLongerThanFive rootFull then "Too much trouble" else greeting root
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment