From 3b806d0bb7ca0602e93ed795d352375ed20834c1 Mon Sep 17 00:00:00 2001 From: John David Pressman Date: Tue, 7 Jan 2020 07:32:44 +0000 Subject: [PATCH] [LIGO-342] Add big maps to docs, change example used for ordinary maps --- .../docs/language-basics/maps-records.md | 287 +++++++++++++++--- 1 file changed, 244 insertions(+), 43 deletions(-) diff --git a/gitlab-pages/docs/language-basics/maps-records.md b/gitlab-pages/docs/language-basics/maps-records.md index e56160d3f..93bf51fde 100644 --- a/gitlab-pages/docs/language-basics/maps-records.md +++ b/gitlab-pages/docs/language-basics/maps-records.md @@ -14,17 +14,20 @@ Here's how a custom map type is defined: ```pascaligo -type ledger is map(address, tez); +type move is (int * int); +type moveset is map(address, move); ``` ```cameligo -type ledger = (address, tez) map +type move = int * int +type moveset = (address, move) map ``` ```reasonligo -type ledger = map(address, tez); +type move = (int, int); +type moveset = map(address, move); ``` @@ -35,9 +38,9 @@ And here's how a map value is populated: ```pascaligo -const ledger: ledger = map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> 1000mutez; - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> 2000mutez; +const moves: moveset = map + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1, 2); + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0, 3); end ``` > Notice the `->` between the key and its value and `;` to separate individual map entries. @@ -47,9 +50,9 @@ end ```cameligo -let ledger: ledger = Map.literal - [ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), 1000mutez) ; - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), 2000mutez) ; +let moves: moveset = Map.literal + [ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1, 2)) ; + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0, 3)) ; ] ``` > Map.literal constructs the map from a list of key-value pair tuples, `(, )`. @@ -60,10 +63,10 @@ let ledger: ledger = Map.literal ```reasonligo -let ledger: ledger = +let moves: moveset = Map.literal([ - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, 1000mutez), - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, 2000mutez), + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1, 2)), + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0, 3)), ]); ``` > Map.literal constructs the map from a list of key-value pair tuples, `(, )`. @@ -74,25 +77,25 @@ let ledger: ledger = ### Accessing map values by key -If we want to access a balance from our ledger above, we can use the `[]` operator/accessor to read the associated `tez` value. However, the value we'll get will be wrapped as an optional; in our case `option(tez)`. Here's an example: +If we want to access a move from our moveset above, we can use the `[]` operator/accessor to read the associated `move` value. However, the value we'll get will be wrapped as an optional; in our case `option(move)`. Here's an example: ```pascaligo -const balance: option(tez) = ledger[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]; +const balance: option(move) = moves[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]; ``` ```cameligo -let balance: tez option = Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) ledger +let balance: move option = Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves ``` ```reasonligo -let balance: option(tez) = - Map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, ledger); +let balance: option(move) = + Map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); ``` @@ -103,24 +106,61 @@ Accessing a value in a map yields an option, however you can also get the value ```pascaligo -const balance: tez = get_force(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), ledger); +const balance: move = get_force(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves); ``` ```cameligo -let balance: tez = Map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) ledger +let balance: move = Map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves ``` ```reasonligo -let balance: tez = - Map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, ledger); +let balance: move = + Map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); ``` +### Updating the contents of a map + + + + + +The values of a PascaLIGO map can be updated using the ordinary assignment syntax: + +```pascaligo + +function set_ (var m: moveset) : moveset is + block { + m[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9); + } with m +``` + + + +We can update a map in CameLIGO using the `Map.update` built-in: + +```cameligo + +let updated_map: moveset = Map.update ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) (Some (4,9)) moves +``` + + + +We can update a map in ReasonLIGO using the `Map.update` built-in: + +```reasonligo + +let updated_map: moveset = Map.update(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); +``` + + + + ### Iteration over the contents of a map There are three kinds of iteration on LIGO maps, `iter`, `map` and `fold`. `iter` @@ -132,24 +172,24 @@ otherwise. ```pascaligo -function iter_op (const m : ledger) : unit is +function iter_op (const m : moveset) : unit is block { - function aggregate (const i : address ; const j : tez) : unit is block - { if (j > 100mutez) then skip else failwith("fail") } with unit ; + function aggregate (const i : address ; const j : move) : unit is block + { if (j.1 > 1) then skip else failwith("fail") } with unit ; } with map_iter(aggregate, m) ; ``` ```cameligo -let iter_op (m : ledger) : unit = - let assert_eq = fun (i: address) (j: tez) -> assert (j > 100tz) +let iter_op (m : moveset) : unit = + let assert_eq = fun (i: address) (j: move) -> assert (j.0 > 1) in Map.iter assert_eq m ``` ```reasonligo -let iter_op = (m: ledger): unit => { - let assert_eq = (i: address, j: tez) => assert(j > 100mutez); +let iter_op = (m: moveset): unit => { + let assert_eq = (i: address, j: move) => assert(j[0] > 1); Map.iter(assert_eq, m); }; ``` @@ -160,23 +200,23 @@ let iter_op = (m: ledger): unit => { ```pascaligo -function map_op (const m : ledger) : ledger is +function map_op (const m : moveset) : moveset is block { - function increment (const i : address ; const j : tez) : tez is block { skip } with j + 1mutez ; + function increment (const i : address ; const j : move) : move is block { skip } with (j.0, j.1 + 1) ; } with map_map(increment, m) ; ``` ```cameligo -let map_op (m : ledger) : ledger = - let increment = fun (_: address) (j: tez) -> j + 1tz +let map_op (m : moveset) : moveset = + let increment = fun (_: address) (j: move) -> (j.0, j.1 + 1) in Map.map increment m ``` ```reasonligo -let map_op = (m: ledger): ledger => { - let increment = (ignore: address, j: tez) => j + 1tz; +let map_op = (m: moveset): moveset => { + let increment = (ignore: address, j: move) => (j[0], j[1] + 1); Map.map(increment, m); }; ``` @@ -194,30 +234,191 @@ It eventually returns the result of combining all the elements. ```pascaligo -function fold_op (const m : ledger) : tez is +function fold_op (const m : moveset) : int is block { - function aggregate (const j : tez ; const cur : (address * tez)) : tez is j + cur.1 ; - } with map_fold(aggregate, m , 10mutez) + function aggregate (const j : int ; const cur : (address * (int * int))) : int is j + cur.1.1 ; + } with map_fold(aggregate, m , 5) ``` ```cameligo -let fold_op (m : ledger) : ledger = - let aggregate = fun (j: tez) (cur: address * tez) -> j + cur.1 in - Map.fold aggregate m 10tz +let fold_op (m : moveset) : moveset = + let aggregate = fun (j: int) (cur: address * (int * int)) -> j + cur.1.1 in + Map.fold aggregate m 5 ``` ```reasonligo -let fold_op = (m: ledger): ledger => { - let aggregate = (j: tez, cur: (address, tez)) => j + cur[1]; - Map.fold(aggregate, m, 10tz); +let fold_op = (m: moveset): moveset => { + let aggregate = (j: int, cur: (address, (int,int))) => j + cur[1][1]; + Map.fold(aggregate, m, 5); }; ``` +## Big Maps + +Ordinary maps are fine for contracts with a finite lifespan or a bounded number +of users. For many contracts however, the intention is to have a map hold *many* +entries, potentially millions or billions. The cost of loading these entries into +the environment each time a user executes the contract would eventually become +too expensive were it not for big maps. Big maps are a data structure offered by +Tezos which handles the scaling concerns for us. In LIGO, the interface for big +maps is analogous to the one used for ordinary maps. + +Here's how we define a big map: + + + +```pascaligo +type move is (int * int); +type moveset is big_map(address, move); +``` + + +```cameligo +type move = int * int +type moveset = (address, move) big_map +``` + + +```reasonligo +type move = (int, int); +type moveset = big_map(address, move); +``` + + + +And here's how a map value is populated: + + + + +```pascaligo +const moves: moveset = big_map + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1, 2); + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0, 3); +end +``` +> Notice the `->` between the key and its value and `;` to separate individual map entries. +> +> `("": address)` means that we type-cast a string into an address. + + + +```cameligo +let moves: moveset = Big_map.literal + [ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1, 2)) ; + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0, 3)) ; + ] +``` +> Big_map.literal constructs the map from a list of key-value pair tuples, `(, )`. +> Note also the `;` to separate individual map entries. +> +> `("": address)` means that we type-cast a string into an address. + + + +```reasonligo +let moves: moveset = + Big_map.literal([ + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1, 2)), + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0, 3)), + ]); +``` +> Big_map.literal constructs the map from a list of key-value pair tuples, `(, )`. +> +> `("": address)` means that we type-cast a string into an address. + + + +### Accessing map values by key + +If we want to access a move from our moveset above, we can use the `[]` operator/accessor to read the associated `move` value. However, the value we'll get will be wrapped as an optional; in our case `option(move)`. Here's an example: + + + +```pascaligo +const balance: option(move) = moves[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]; +``` + + + +```cameligo +let balance: move option = Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves +``` + + + +```reasonligo +let balance: option(move) = + Big_map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); +``` + + +#### Obtaining a map value forcefully + +Accessing a value in a map yields an option, however you can also get the value directly: + + + +```pascaligo +const balance: move = get_force(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves); +``` + + + +```cameligo +let balance: move = Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves +``` + + + +```reasonligo +let balance: move = Big_map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); +``` + + + +### Updating the contents of a big map + + + + + +The values of a PascaLIGO big map can be updated using the ordinary assignment syntax: + +```pascaligo + +function set_ (var m: moveset) : moveset is + block { + m[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9); + } with m +``` + + + +We can update a big map in CameLIGO using the `Big_map.update` built-in: + +```cameligo + +let updated_map: moveset = + Big_map.update ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) (Some (4,9)) moves +``` + + + +We can update a big map in ReasonLIGO using the `Big_map.update` built-in: + +```reasonligo +let updated_map: moveset = + Big_map.update(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); +``` + + + ## Records Records are a construct introduced in LIGO, and are not natively available in Michelson. The LIGO compiler translates records into Michelson `Pairs`.