Nanyx

Constrained values

Protected nominal types whose only constructors check an invariant.

module constraints

-- Nanyx has no refinement types. A value that must satisfy an invariant is
-- modelled as a protected nominal type: the representation is hidden outside
-- this module, so the only way to obtain a value is through a constructor
-- that checks the invariant.

export type $Email = private string

export def $Email.parse: string -> #some($Email) | #invalidEmail = { s ->
  if s \contains("@") and s \contains(".") then #some($Email(s))
  else #invalidEmail
}

export type $Nat = private int

export def $Nat.of: int -> #some($Nat) | #negative = { n ->
  if n >= 0 then #some($Nat(n)) else #negative
}

export type $OrderedPair = private (x: int, y: int)

export def $OrderedPair.of: (int, int) -> #some($OrderedPair) | #notOrdered = { x, y ->
  if x < y then #some($OrderedPair(x = x, y = y)) else #notOrdered
}

-- Readers can inspect values but never construct an invalid one
export def $Email.value: $Email -> string = { $Email(s) -> s }
export def $Nat.value: $Nat -> int = { $Nat(n) -> n }

This program lives in the repository as examples/constraints.nyx.