ligo/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md
2020-02-05 16:28:40 +01:00

5.5 KiB

id title
unit-option-pattern-matching Unit, Option, Pattern matching

Optionals are a pervasive programing pattern in OCaml. Since Michelson and LIGO are both inspired by OCaml, optional types are available in LIGO as well. Similarly, OCaml features a unit type, and LIGO features it as well. Both the option type and the unit types are instances of a more general kind of types: variant types (sometimes called sum types).

The unit type

The unit type in Michelson or LIGO is a predefined type that contains only one value that carries no information. It is used when no relevant information is required or produced. Here is how it used.

💡 Units come in handy when we try pattern matching on custom variants below.

In PascaLIGO, the unique value of the unit type is Unit.

const n : unit = Unit

In CameLIGO, the unique value of the unit type is (), following the OCaml convention.

let n : unit = ()

In ReasonLIGO, the unique value of the unit type is (), following the OCaml convention.

let n : unit = ();

Variant types

A variant type is a user-defined or a built-in type (in case of options) that defines a type by cases, so a value of a variant type is either this, or that or... The simplest variant type is equivalent to the enumerated types found in Java, C++, JavaScript etc.

Here is how we define a coin as being either head or tail (and nothing else):

type coin is Head | Tail
const head : coin = Head (Unit) // Unit needed because of a bug
const tail : coin = Tail (Unit) // Unit needed because of a bug
type coin = Head | Tail
let head : coin = Head
let tail : coin = Tail
type coin = | Head | Tail;
let head : coin = Head;
let tail : coin = Tail;

The names Head and Tail in the definition of the type coin are called data constructors, or variants.

In general, it is interesting for variants to carry some information, and thus go beyond enumerated types. In the following, we show how to define different kinds of users of a system.

type id is nat

type user is
  Admin   of id
| Manager of id
| Guest

const u : user = Admin (1000n)
const g : user = Guest (Unit) // Unit needed because of a bug
type id = nat

type user =
  Admin   of id
| Manager of id
| Guest

let u : user = Admin 1000n
let g : user = Guest
type id = nat;

type user =
| Admin   (id)
| Manager (id)
| Guest;

let u : user = Admin (1000n);
let g : user = Guest;

Defining a variant can be extremely useful for building semantically appealing contracts. We will learn how to use variants for "logic purposes"' shortly.

Optional values

The option type is a predefined variant type that is used to express whether there is a value of some type or none. This is especially useful when calling a partial function, that is, a function that is not defined for some inputs. In that case, the value of the option type would be None, otherwise Some (v), where v is some meaningful value of any type. An example in arithmetic is the division operation:

function div (const a : nat; const b : nat) : option (nat) is
  if b = 0n then (None: option (nat)) else Some (a/b)
let div (a, b : nat * nat) : nat option =
  if b = 0n then (None: nat option) else Some (a/b)
let div = ((a, b) : (nat, nat)) : option (nat) =>
  if (b == 0n) { (None: option (nat)); } else { Some (a/b); };

Pattern matching

Pattern matching is similiar to the switch construct in Javascript, and can be used to route the program's control flow based on the value of a variant. Consider for example the definition of a function flip that flips a coin, as defined above.

type coin is Head | Tail

function flip (const c : coin) : coin is
  case c of
    Head -> Tail (Unit) // Unit needed because of a bug
  | Tail -> Head (Unit) // Unit needed because of a bug
  end

You can call the function flip by using the LIGO compiler like so:

ligo run-function
gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo
flip "(Head (Unit))"
# Outputs: Tail(Unit)
type coin = Head | Tail

let flip (c : coin) : coin =
  match c with
    Head -> Tail
  | Tail -> Head

You can call the function flip by using the LIGO compiler like so:

ligo run-function
gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.mligo
flip Head
# Outputs: Tail(Unit)
type coin = | Head | Tail;

let flip = (c : coin) : coin =>
  switch (c) {
  | Head => Tail
  | Tail => Head
  };

You can call the function flip by using the LIGO compiler like so:

ligo run-function
gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.religo
flip Head
# Outputs: Tail(Unit)