Skip to main content

Algebraic data types

In functional programming and type theory, an algebraic data type (ADT) is a kind of composite type, formed of two or more other primitive types.

type MyAdt =  | MyConstructor1 of Int32 String  | MyConstructor2 of ByStr20  | MyConstructor3

An ADT is defined using one or more constructors (not to be confused with constructors from object-oriented languages). A value of type MyAdt is constructed using exactly one of these constructors. In this example, a MyAdt value constructed using MyConstructor1 will contain a tuple comprised of an Int32 value and a String value. A MyAdt value constructed using MyConstructor3 on the other hand will not contain any further values inside it.

The example we saw previously defines an ADT whose composite types are fixed. A polymorphic ADT allows using type variables in place of concrete constituent types, for substitution later. Let's see an example.

type 'A MyPAdt =  | MyPConstructor1 of 'A String  | MyPConstructor2 of ByStr20  | MyPConstructor3

MyPAdt is a polymorphic ADT with a type parameter (variable) 'A. When MyPAdt is, for example, instantiated with the type parameter 'A set to Int32, then MyPAdt will share the same structure as MyAdt. List, the Scilla built-in ADT is, for example, a polymorphic ADT. When we write List Int32, it means that we have instantiated List ADT with Int32. This built-in ADT, if it wasn't built-in, would look like:

type 'A List =  | Cons of 'A ('A List)  | Nil

At the moment, Scilla does not support user-defined polymorphic ADTs. Only the built-in ADTs List, Pair and Option are polymorphic. Users can only define concrete ADTs such as MyAdt.

ADT's in Data Structures#

:::warn You cannot remote state read a data structure which holds an ADT. :::

Bool#

my_bool = True

Option#

let none_value = None {ByStr20}
field staging_owner: Option ByStr20 = none_value
transition Test(optional_staging : Option ByStr20)  match new_staging with    | Some staging_value =>      staging_owner := staging_value    | None =>  endend

List#

field state_list_users : List ByStr20 = Nil {ByStr20}transition Test()  current_list <- state_list_users;  new_user = Cons {ByStr20} _sender current_list;  state_list_users := new_userend

Pair#

field state_user_pairs : Map ByStr20 (Pair ByStr20 Uint128)
transition Test(delegator: ByStr20, user: ByStr20, amount: Uint128)  pair_example = Pair {ByStr20 Uint128} _sender user;  state_user_pairs[delegator] : pair_exampleend

Nat#

field state_nat : Nat = Zero
current_nat <- state_nat;
let x =  let nat_plus_one  = Succ current_nat

User-defined#

type SwapDirection = | ZilToToken | TokenToZil
procedure Example(direction: SwapDirection)    match direction with      | ZilToToken =>       | TokenToZil =>     endend
type Pool = | Pool of Uint128 Uint128 
procedure Example(pool: Pool)  match pool with    | Pool x y =>  endend
type Game = | Game of (Option ByStr20) Uint128
procedure Example(game: Game)  match game with    | Game maybe_bystr game_int =>      (* At this point you have the Optional and the int value*)      match maybe_bystr with        | Some some_bystr =>          (* At this point you have Some bystr20 value and the int value *)        | None =>          (* At this point you have only the int value*)      end  endend

ADT example contracts#

Store ADT as List (Pair Parcel Uint32)#

scilla_version 0(****************************************************************************)(*   user defined algebraic data type (ADT) examples:                       *)(*   a Parcel with a content consisting of one or two Item(s)               *)(*   A single Item can be a Shirt or a Barbell and has a weight             *)(*   A Parcel can have one Item or two Items in it and depending on the     *)(*   total weight has a cost to ship assigned to it                         *)(****************************************************************************)library Adt
type Item = (* constructor argument is the weight of the item *)  | Shirt of Uint32  | Barbell of Uint32
type Parcel =  | OneContent of Item (* a parcel with a single item as content *)  | TwoContents of Item Item (* a parcel can be filled with 2 items *)
let weight_of_item = fun(i: Item) => (* how much an item weighs *)  match i with  | Shirt w => w  | Barbell w => w  end
let cost_per_weight = Uint32 5
let cost_of_parcel = fun(p: Parcel) => (* cost to ship the parcel *)  let weight =    match p with    | OneContent c => (* parcel has only one item as content *)        weight_of_item c    | TwoContents c1 c2 => (* parcel has two items as content *)        let w1 = weight_of_item c1 in        let w2 = weight_of_item c2 in        builtin add w1 w2 (* total weight is sum of the two *)    end in  builtin mul weight cost_per_weight (* cost is total weight times cost_per_weight *)

contract Adt()
(* mutable fields declarations *)field parcels : List (Pair Parcel Uint32) = Nil {(Pair Parcel Uint32)} (* a list of parcels and their cost to ship *)
procedure ComputeCostAndAdd(p: Parcel)  (* compute cost to ship the new parcel *)  cost = cost_of_parcel p;  (* add it to the list of parcels *)  l <- parcels;  pair = Pair {Parcel Uint32} p cost;  new_list = Cons {(Pair Parcel Uint32)} pair l; (* front insert *)  parcels := new_list;  ev = {_eventname : "AddToListOfParcelsSuccess"; cost_to_ship: cost; parcels: new_list};  event evend
(* add parcels with different items to the list of parcels *)transition AddParcelWithShirt(weight: Uint32)  c = Shirt weight;  p = OneContent c;  ComputeCostAndAdd pend
transition AddParcelWithBarbell(weight: Uint32)  c = Barbell weight;  p = OneContent c;  ComputeCostAndAdd pend
transition AddParcelWithTwoShirts(weight1: Uint32, weight2: Uint32)  c1 = Shirt weight1;  c2 = Shirt weight2;  p = TwoContents c1 c2;  ComputeCostAndAdd pend
transition AddParcelWithShirtAndBarbell(weightS: Uint32, weightB: Uint32)  c1 = Shirt weightS;  c2 = Barbell weightB;  p = TwoContents c1 c2;  ComputeCostAndAdd pend

Store ADT as Map ByStr20 Player#

scilla_version 0(************************************************************************)(* a map of ByStr20 (address) to a user defined algebraic data type     *)(*   the ADT is a Player with an age that either plays tennis or runs   *)(************************************************************************)library ADTMap
type Sport =  | Tennis  | Run
type Player = | Player of Uint32 Sport (* Age and preferred Sport *)

contract ADTMap()
field players : Map ByStr20 Player = Emp ByStr20 Player 
procedure Add(age: Uint32, sport: Sport)  player = Player age sport;  players[_sender] := playerend
transition Remove()  delete players[_sender]end

Further reading#

adt.scilla

adtmap.scilla

Interfacing.scilla

Scilla Documentation - User Defined ADT