Version: 0.17.0

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.

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

const n : unit = Unit // Note the capital letter

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
const tail : coin = Tail

The names Head and Tail in the definition of the type coin are called data constructors, or variants. In this particular, they carry no information beyond their names, so they are called constant constructors.

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

In PascaLIGO, a constant constructor is equivalent to the same constructor taking an argument of type unit, so, for example, Guest is the same value as Guest (unit).

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)

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, record, tuple, or list.

A component of a pattern can be discarded by using a wildcard _ instead of a variable name.

LIGO will warn about unused variables bound in patterns in the same way that function arguments are warned about. Variable names beginning with _ can be used as a binder to prevent warnings.

Match on variants#

Here is a function that transforms a color variant type to an int.

type color is
| RGB of int * int * int
| Gray of int
| Default
function int_of_color (const c : color) : int is
case c of
| RGB (r,g,b) -> 16 + b + g * 6 + r * 36
| Gray (i) -> 232 + i
| Default -> 0

Match on records or tuples#

Fields of records and components of tuples can be destructured. Record pattern variables can be renamed.

type my_record is record [ a : int ; b : nat ; c : string ]
type my_tuple is (int * nat * string)
function on_record (const v : my_record) : int is
case v of
record [ a ; b = b_renamed ; c = _ ] -> a + int(b_renamed)
function on_tuple (const v : my_tuple) : int is
case v of
| ( x , y , _ ) -> x + int(y)

Match on lists#

function weird_length (const v : list(int)) : int is
case v of
| nil -> -1
| list [ a; b ; c] -> -2
| x -> int (List.length (x))

Deep patterns#

Pattern matching can also be used for nested patterns.

type complex_t is record [ a : option(list(int)) ; b : list(int) ]
function complex (const x:complex_t ; const y:complex_t) is
case (x,y) of
| (record [ a=None;b=_] , record [ a = _ ; b = _ ]) -> -1
| (record [ a=_;b=_] , record [ a = Some (nil) ; b = (hd#tl) ]) -> hd
| (record [ a=_;b=_] , record [ a = Some ((hd#tl)) ; b = nil ]) -> hd
| (record [ a=Some (a);b=_] , _) -> int ( List.length(a) )