ligo/gitlab-pages/docs/language-basics/maps-records.md

939 lines
24 KiB
Markdown
Raw Normal View History

---
id: maps-records
2020-02-05 19:28:40 +04:00
title: Records and Maps
---
2020-01-28 18:13:50 +04:00
So far we have seen pretty basic data types. LIGO also offers more
2020-02-05 19:28:40 +04:00
complex built-in constructs, such as *records* and *maps*.
2020-02-05 19:28:40 +04:00
## Records
2020-02-05 19:28:40 +04:00
Records are one way data of different types can be packed into a
single type. A record is made of a set of *fields*, which are made of
a *field name* and a *field type*. Given a value of a record type, the
value bound to a field can be accessed by giving its field name to a
special operator (`.`).
2020-02-05 19:28:40 +04:00
Let us first consider and example of record type declaration.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=records1
2020-02-05 19:28:40 +04:00
type user is
record [
id : nat;
is_admin : bool;
name : string
]
```
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=records1
2020-02-05 19:28:40 +04:00
type user = {
id : nat;
is_admin : bool;
name : string
}
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=records1
2020-02-05 19:28:40 +04:00
type user = {
id : nat,
is_admin : bool,
name : string
};
2019-12-10 17:47:31 +04:00
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
And here is how a record value is defined:
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=records1
2020-02-05 19:28:40 +04:00
const alice : user =
record [
id = 1n;
is_admin = True;
name = "Alice"
]
```
2019-12-10 17:47:31 +04:00
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=records1
2020-02-05 19:28:40 +04:00
let alice : user = {
id = 1n;
is_admin = true;
name = "Alice"
}
2019-12-11 13:34:08 +04:00
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=records1
2020-02-05 19:28:40 +04:00
let alice : user = {
id : 1n,
is_admin : true,
name : "Alice"
};
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
### Accessing Record Fields
2020-02-10 22:07:20 +04:00
If we want the contents of a given field, we use the (`.`) infix
2020-02-05 19:28:40 +04:00
operator, like so:
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=records1
2020-02-05 19:28:40 +04:00
const alice_admin : bool = alice.is_admin
```
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=records1
2020-02-05 19:28:40 +04:00
let alice_admin : bool = alice.is_admin
```
2019-12-10 17:47:31 +04:00
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=records1
2020-02-12 01:29:12 +04:00
let alice_admin : bool = alice.is_admin;
2019-12-10 17:47:31 +04:00
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
### Functional Updates
Given a record value, it is a common design pattern to update only a
small number of its fields. Instead of copying the fields that are
unchanged, LIGO offers a way to only update the fields that are
modified.
2020-02-05 19:28:40 +04:00
One way to understand the update of record values is the *functional
update*. The idea is to have an *expression* whose value is the
2020-02-12 01:29:12 +04:00
updated record.
2020-02-05 19:28:40 +04:00
Let us consider defining a function that translates three-dimensional
points on a plane.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-12 01:29:12 +04:00
In PascaLIGO, the shape of that expression is `<record variable> with
<record value>`. The record variable is the record to update and the
record value is the update itself.
```pascaligo group=records2
2020-02-05 19:28:40 +04:00
type point is record [x : int; y : int; z : int]
type vector is record [dx : int; dy : int]
2020-02-05 19:28:40 +04:00
const origin : point = record [x = 0; y = 0; z = 0]
2020-02-05 19:28:40 +04:00
function xy_translate (var p : point; const vec : vector) : point is
p with record [x = p.x + vec.dx; y = p.y + vec.dy]
```
2020-02-05 19:28:40 +04:00
You can call the function `xy_translate` defined above by running the
following command of the shell:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/maps-records/record_update.ligo
translate "(record [x=2;y=3;z=1], record [dx=3;dy=4])"
# Outputs: {z = 1 , y = 7 , x = 5}
2019-12-10 17:47:31 +04:00
```
2020-02-05 19:28:40 +04:00
You have to understand that `p` has not been changed by the functional
update: a namless new version of it has been created and returned by
the blockless function.
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
2020-02-05 19:28:40 +04:00
The syntax for the functional updates of record in CameLIGO follows
that of OCaml:
```cameligo group=records2
2020-02-05 19:28:40 +04:00
type point = {x : int; y : int; z : int}
type vector = {dx : int; dy : int}
2020-02-05 19:28:40 +04:00
let origin : point = {x = 0; y = 0; z = 0}
2020-02-05 19:28:40 +04:00
let xy_translate (p, vec : point * vector) : point =
{p with x = p.x + vec.dx; y = p.y + vec.dy}
```
2020-02-05 19:28:40 +04:00
You can call the function `xy_translate` defined above by running the
following command of the shell:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/maps-records/record_update.mligo
xy_translate "({x=2;y=3;z=1}, {dx=3;dy=4})"
# Outputs: {z = 1 , y = 7 , x = 5}
```
2020-02-05 19:28:40 +04:00
> You have to understand that `p` has not been changed by the
> functional update: a nameless new version of it has been created and
> returned.
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
2020-02-05 19:28:40 +04:00
The syntax for the functional updates of record in ReasonLIGO follows
2020-02-12 01:29:12 +04:00
that of ReasonML:
```reasonligo group=records2
2020-02-05 19:28:40 +04:00
type point = {x : int, y : int, z : int};
type vector = {dx : int, dy : int};
2020-02-05 19:28:40 +04:00
let origin : point = {x : 0, y : 0, z : 0};
2020-02-05 19:28:40 +04:00
let xy_translate = ((p, vec) : (point, vector)) : point =>
{...p, x : p.x + vec.dx, y : p.y + vec.dy};
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
You can call the function `xy_translate` defined above by running the
2020-02-05 19:28:40 +04:00
following command of the shell:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/maps-records/record_update.religo
xy_translate "({x:2,y:3,z:1}, {dx:3,dy:4})"
# Outputs: {z = 1 , y = 7 , x = 5}
```
2020-02-05 19:28:40 +04:00
You have to understand that `p` has not been changed by the functional
update: a nameless new version of it has been created and returned.
2020-02-12 01:29:12 +04:00
### Record Patches
2020-02-05 19:28:40 +04:00
Another way to understand what it means to update a record value is to
make sure that any further reference to the value afterwards will
exhibit the modification. This is called a `patch` and this is only
possible in PascaLIGO, because a patch is an *instruction*, therefore
we can only use it in a block. Similarly to a *functional update*, a
patch takes a record to be updated and a record with a subset of the
fields to update, then applies the latter to the former (hence the
name "patch").
2020-02-05 19:28:40 +04:00
Let us consider defining a function that translates three-dimensional
points on a plane.
```pascaligo group=records3
2020-02-05 19:28:40 +04:00
type point is record [x : int; y : int; z : int]
type vector is record [dx : int; dy : int]
const origin : point = record [x = 0; y = 0; z = 0]
function xy_translate (var p : point; const vec : vector) : point is
block {
2020-02-05 19:28:40 +04:00
patch p with record [x = p.x + vec.dx];
patch p with record [y = p.y + vec.dy]
} with p
```
2020-02-05 19:28:40 +04:00
You can call the function `xy_translate` defined above by running the
following command of the shell:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/maps-records/record_patch.ligo
xy_translate "(record [x=2;y=3;z=1], record [dx=3;dy=4])"
# Outputs: {z = 1 , y = 7 , x = 5}
```
2019-12-10 17:47:31 +04:00
2020-02-05 19:28:40 +04:00
Of course, we can actually translate the point with only one `patch`,
as the previous example was meant to show that, after the first patch,
the value of `p` indeed changed. So, a shorter version would be
```pascaligo group=records4
2020-02-05 19:28:40 +04:00
type point is record [x : int; y : int; z : int]
type vector is record [dx : int; dy : int]
2020-02-05 19:28:40 +04:00
const origin : point = record [x = 0; y = 0; z = 0]
function xy_translate (var p : point; const vec : vector) : point is
block {
2020-02-05 19:28:40 +04:00
patch p with record [x = p.x + vec.dx; y = p.y + vec.dy]
} with p
```
2020-02-05 19:28:40 +04:00
You can call the new function `xy_translate` defined above by running the
following command of the shell:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/maps-records/record_patch2.ligo
xy_translate "(record [x=2;y=3;z=1], record [dx=3;dy=4])"
# Outputs: {z = 1 , y = 7 , x = 5}
```
2019-12-10 17:47:31 +04:00
2020-02-05 19:28:40 +04:00
Record patches can actually be simulated with functional updates. All
we have to do is *declare a new record value with the same name as the
one we want to update* and use a functional update, like so:
```pascaligo group=records5
2020-02-05 19:28:40 +04:00
type point is record [x : int; y : int; z : int]
type vector is record [dx : int; dy : int]
2020-02-05 19:28:40 +04:00
const origin : point = record [x = 0; y = 0; z = 0]
2020-02-10 22:07:20 +04:00
function xy_translate (var p : point; const vec : vector) : point is
block {
const p : point = p with record [x = p.x + vec.dx; y = p.y + vec.dy]
} with p
```
2020-02-05 19:28:40 +04:00
You can call the new function `xy_translate` defined above by running the
following command of the shell:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/maps-records/record_simu.ligo
xy_translate "(record [x=2;y=3;z=1], record [dx=3;dy=4])"
# Outputs: {z = 1 , y = 7 , x = 5}
```
2020-02-05 19:28:40 +04:00
The hiding of a variable by another (here `p`) is called `shadowing`.
2019-12-10 17:47:31 +04:00
2020-02-05 19:28:40 +04:00
## Maps
*Maps* are a data structure which associate values of the same type to
values of the same type. The former are called *key* and the latter
*values*. Together they make up a *binding*. An additional requirement
is that the type of the keys must be *comparable*, in the Michelson
sense.
### Declaring a Map
2020-02-05 19:28:40 +04:00
Here is how a custom map from addresses to a pair of integers is
defined.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=maps
2020-02-05 19:28:40 +04:00
type move is int * int
type register is map (address, move)
```
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=maps
type move = int * int
2020-02-05 19:28:40 +04:00
type register = (address, move) map
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=maps
type move = (int, int);
2020-02-05 19:28:40 +04:00
type register = map (address, move);
```
<!--END_DOCUSAURUS_CODE_TABS-->
### Creating an Empty Map
Here is how to create an empty map.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=maps
const empty : register = map []
```
<!--CameLIGO-->
```cameligo group=maps
let empty : register = Map.empty
```
<!--ReasonLIGO-->
```reasonligo group=maps
let empty : register = Map.empty
```
<!--END_DOCUSAURUS_CODE_TABS-->
### Creating a Non-empty Map
And here is how to create a non-empty map value:
<!--DOCUSAURUS_CODE_TABS-->
<!--PascaLIGO-->
```pascaligo group=maps
2020-02-05 19:28:40 +04:00
const moves : register =
map [
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2);
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (0,3)]
```
Notice the `->` between the key and its value and `;` to separate
individual map entries. The annotated value `("<string value>" :
address)` means that we cast a string into an address. Also, `map` is
a keyword.
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=maps
2020-02-05 19:28:40 +04:00
let moves : register =
Map.literal [
(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2));
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))]
```
The `Map.literal` predefined function builds a map from a list of
key-value pair tuples, `(<key>, <value>)`. Note also the `;` to
separate individual map entries. `("<string value>": address)` means
that we type-cast a string into an address. -->
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=maps
2020-02-05 19:28:40 +04:00
let moves : register =
Map.literal ([
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)),
2020-02-06 14:47:41 +04:00
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]);
```
2020-02-05 19:28:40 +04:00
The `Map.literal` predefined function builds a map from a list of
key-value pair tuples, `(<key>, <value>)`. Note also the `;` to
separate individual map entries. `("<string value>": address)` means
that we type-cast a string into an address. -->
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
### Accessing Map Bindings
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-12 01:29:12 +04:00
In PascaLIGO, we can use the postfix `[]` operator to read the `move`
value associated to a given key (`address` here) in the register. Here
is an example:
```pascaligo group=maps
2020-02-06 14:47:41 +04:00
const my_balance : option (move) =
moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)]
```
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=maps
2020-01-28 18:13:50 +04:00
let my_balance : move option =
2020-02-05 19:28:40 +04:00
Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=maps
2020-02-05 19:28:40 +04:00
let my_balance : option (move) =
Map.find_opt (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), moves);
```
<!--END_DOCUSAURUS_CODE_TABS-->
Notice how the value we read is an optional value: this is to force
the reader to account for a missing key in the map. This requires
*pattern matching*.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=maps
2020-02-05 19:28:40 +04:00
function force_access (const key : address; const moves : register) : move is
case moves[key] of
Some (move) -> move
| None -> (failwith ("No move.") : move)
end
```
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=maps
2020-02-05 19:28:40 +04:00
let force_access (key, moves : address * register) : move =
match Map.find_opt key moves with
Some move -> move
| None -> (failwith "No move." : move)
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=maps
2020-02-06 14:47:41 +04:00
let force_access = ((key, moves) : (address, register)) : move => {
switch (Map.find_opt (key, moves)) {
| Some (move) => move
| None => failwith ("No move.") : move
}
2020-02-05 19:28:40 +04:00
};
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
### Updating a Map
Given a map, we may want to add a new binding, remove one, or modify
one by changing the value associated to an already existing key. All
2020-02-05 19:28:40 +04:00
those operations are called *updates*.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-05 19:28:40 +04:00
The values of a PascaLIGO map can be updated using the usual
assignment syntax `<map variable>[<key>] := <new value>`. Let us
consider an example.
```pascaligo group=maps
2020-02-05 19:28:40 +04:00
function assign (var m : register) : register is
block {
m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9)
} with m
```
2020-02-05 19:28:40 +04:00
If multiple bindings need to be updated, PascaLIGO offers a *patch
instruction* for maps, similar to that for records.
```pascaligo group=maps
2020-02-05 19:28:40 +04:00
function assignments (var m : register) : register is
block {
patch m with map [
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (4,9);
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2)
]
} with m
```
See further for the removal of bindings.
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
2020-02-05 19:28:40 +04:00
We can update a binding in a map in CameLIGO by means of the
`Map.update` built-in function:
```cameligo group=maps
2020-02-05 19:28:40 +04:00
let assign (m : register) : register =
Map.update
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) m
```
Notice the optional value `Some (4,9)` instead of `(4,9)`. If we had
use `None` instead, that would have meant that the binding is removed.
As a particular case, we can only add a key and its associated value.
```cameligo group=maps
let add (m : register) : register =
Map.add
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (4,9) m
```
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
2020-02-05 19:28:40 +04:00
We can update a binding in a map in ReasonLIGO by means of the
`Map.update` built-in function:
```reasonligo group=maps
2020-02-12 01:29:12 +04:00
let assign = (m : register) : register =>
2020-02-05 19:28:40 +04:00
Map.update
2020-02-12 01:29:12 +04:00
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), Some ((4,9)), m);
```
Notice the optional value `Some (4,9)` instead of `(4,9)`. If we had
use `None` instead, that would have meant that the binding is removed.
As a particular case, we can only add a key and its associated value.
```reasonligo group=maps
let add = (m : register) : register =>
Map.add
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (4,9), m);
```
2020-02-05 19:28:40 +04:00
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
To remove a binding from a map, we need its key.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-05 19:28:40 +04:00
In PascaLIGO, there is a special instruction to remove a binding from
a map.
```pascaligo group=maps
2020-02-05 19:28:40 +04:00
function delete (const key : address; var moves : register) : register is
block {
remove key from map moves
} with moves
```
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
In CameLIGO, we use the predefined function `Map.remove` as follows:
```cameligo group=maps
2020-02-05 19:28:40 +04:00
let delete (key, moves : address * register) : register =
Map.remove key moves
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
In ReasonLIGO, we use the predefined function `Map.remove` as follows:
```reasonligo group=maps
2020-02-12 01:29:12 +04:00
let delete = ((key, moves) : (address, register)) : register =>
2020-02-05 19:28:40 +04:00
Map.remove (key, moves);
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
### Functional Iteration over Maps
2020-02-05 19:28:40 +04:00
A *functional iterator* is a function that traverses a data structure
and calls in turn a given function over the elements of that structure
to compute some value. Another approach is possible in PascaLIGO:
*loops* (see the relevant section).
2020-02-10 22:07:20 +04:00
There are three kinds of functional iterations over LIGO maps: the
*iterated operation*, the *map operation* (not to be confused with the
*map data structure*) and the *fold operation*.
2020-02-12 01:29:12 +04:00
#### Iterated Operation over Maps
2020-02-10 22:07:20 +04:00
The first, the *iterated operation*, is an iteration over the map with
2020-02-05 19:28:40 +04:00
no return value: its only use is to produce side-effects. This can be
useful if for example you would like to check that each value inside
of a map is within a certain range, and fail with an error otherwise.
The predefined functional iterator implementing the iterated operation
over maps is called `Map.iter`. In the following example, the register
of moves is iterated to check that the start of each move is above
`3`.
2020-02-05 19:28:40 +04:00
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=maps
2020-02-05 19:28:40 +04:00
function iter_op (const m : register) : unit is
block {
2020-02-10 22:07:20 +04:00
function iterated (const i : address; const j : move) : unit is
if j.1 > 3 then Unit else (failwith ("Below range.") : unit)
} with Map.iter (iterated, m)
```
> Note that `map_iter` is *deprecated*.
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=maps
2020-02-05 19:28:40 +04:00
let iter_op (m : register) : unit =
2020-02-10 22:07:20 +04:00
let predicate = fun (i,j : address * move) -> assert (j.0 > 3)
in Map.iter predicate m
2020-02-05 19:28:40 +04:00
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=maps
2020-02-05 19:28:40 +04:00
let iter_op = (m : register) : unit => {
2020-02-10 22:07:20 +04:00
let predicate = ((i,j) : (address, move)) => assert (j[0] > 3);
Map.iter (predicate, m);
2020-02-05 19:28:40 +04:00
};
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-12 01:29:12 +04:00
#### Map Operations over Maps
2020-02-10 22:07:20 +04:00
2020-02-05 19:28:40 +04:00
We may want to change all the bindings of a map by applying to them a
2020-02-10 22:07:20 +04:00
function. This is called a *map operation*, not to be confused with
the map data structure. The predefined functional iterator
implementing the map operation over maps is called `Map.map`. In the
following example, we add `1` to the ordinate of the moves in the
register.
2020-02-05 19:28:40 +04:00
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=maps
2020-02-05 19:28:40 +04:00
function map_op (const m : register) : register is
block {
function increment (const i : address; const j : move) : move is
(j.0, j.1 + 1)
} with Map.map (increment, m)
2020-02-05 19:28:40 +04:00
```
> Note that `map_map` is *deprecated*.
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=maps
2020-02-05 19:28:40 +04:00
let map_op (m : register) : register =
let increment = fun (i,j : address * move) -> j.0, j.1 + 1
in Map.map increment m
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=maps
2020-02-05 19:28:40 +04:00
let map_op = (m : register) : register => {
let increment = ((i,j): (address, move)) => (j[0], j[1] + 1);
2020-02-10 22:07:20 +04:00
Map.map (increment, m);
2020-02-05 19:28:40 +04:00
};
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-12 01:29:12 +04:00
#### Folded Operations over Maps
2020-02-10 22:07:20 +04:00
2020-02-12 01:29:12 +04:00
A *folded operation* is the most general of iterations. The folded
2020-02-05 19:28:40 +04:00
function takes two arguments: an *accumulator* and the structure
*element* at hand, with which it then produces a new accumulator. This
2020-02-06 14:47:41 +04:00
enables having a partial result that becomes complete when the
2020-02-05 19:28:40 +04:00
traversal of the data structure is over.
The predefined functional iterator implementing the folded operation
over maps is called `Map.fold` and is used as follows.
2020-02-05 19:28:40 +04:00
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=maps
function fold_op (const m : register) : int is
block {
function folded (const i : int; const j : address * move) : int is
i + j.1.1
} with Map.fold (folded, m, 5)
2020-02-05 19:28:40 +04:00
```
> Note that `map_fold` is *deprecated*.
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=maps
let fold_op (m : register) : int =
2020-02-12 01:29:12 +04:00
let folded = fun (i,j : int * (address * move)) -> i + j.1.1
in Map.fold folded m 5
2020-02-05 19:28:40 +04:00
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=maps
let fold_op = (m : register) : int => {
2020-02-12 01:29:12 +04:00
let folded = ((i,j): (int, (address, move))) => i + j[1][1];
Map.fold (folded, m, 5);
2020-02-05 19:28:40 +04:00
};
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
## Big Maps
2020-02-05 19:28:40 +04:00
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 holding *many* entries, potentially millions of
them. The cost of loading those 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
Michelson which handles the scaling concerns for us. In LIGO, the
interface for big maps is analogous to the one used for ordinary maps.
### Declaring a Map
2020-02-05 19:28:40 +04:00
Here is how we define a big map:
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=big_maps
2020-02-05 19:28:40 +04:00
type move is int * int
type register is big_map (address, move)
```
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
```cameligo group=big_maps
2020-02-05 19:28:40 +04:00
type move = int * int
type register = (address, move) big_map
2019-12-10 17:47:31 +04:00
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
```reasonligo group=big_maps
2020-02-05 19:28:40 +04:00
type move = (int, int);
2020-02-10 22:07:20 +04:00
type register = big_map (address, move);
2020-02-05 19:28:40 +04:00
```
<!--END_DOCUSAURUS_CODE_TABS-->
### Creating an Empty Big Map
Here is how to create an empty big map.
<!--DOCUSAURUS_CODE_TABS-->
<!--PascaLIGO-->
```pascaligo group=big_maps
const empty : register = big_map []
```
<!--CameLIGO-->
```cameligo group=big_maps
let empty : register = Big_map.empty
```
<!--ReasonLIGO-->
```reasonligo group=big_maps
let empty : register = Big_map.empty
```
<!--END_DOCUSAURUS_CODE_TABS-->
### Creating a Non-empty Map
And here is how to create a non-empty map value:
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-05 19:28:40 +04:00
```pascaligo group=big_maps
2020-02-05 19:28:40 +04:00
const moves : register =
big_map [
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2);
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (0,3)]
```
Notice the right arrow `->` between the key and its value and the
semicolon separating individual map entries. The value annotation
`("<string value>" : address)` means that we cast a string into an
address. -->
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
2020-02-05 19:28:40 +04:00
```cameligo group=big_maps
2020-02-05 19:28:40 +04:00
let moves : register =
Big_map.literal [
(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2));
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))]
```
The predefind function `Big_map.literal` constructs a big map from a
list of key-value pairs `(<key>, <value>)`. Note also the semicolon
separating individual map entries. The annotated value `("<string>
value>" : address)` means that we cast a string into an address.
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
2020-02-05 19:28:40 +04:00
```reasonligo group=big_maps
2020-02-05 19:28:40 +04:00
let moves : register =
Big_map.literal ([
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)),
2020-02-06 14:47:41 +04:00
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]);
2019-12-10 17:47:31 +04:00
```
2020-02-05 19:28:40 +04:00
The predefind function `Big_map.literal` constructs a big map from a
list of key-value pairs `(<key>, <value>)`. Note also the semicolon
separating individual map entries. The annotated value `("<string>
value>" : address)` means that we cast a string into an address.
2020-02-05 19:28:40 +04:00
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
### Accessing Values
2020-02-05 19:28:40 +04:00
If we want to access a move from our `register` above, we can use the
postfix `[]` operator to read the associated `move` value. However,
2020-02-10 22:07:20 +04:00
the value we read is an optional value (in our case, of type `option
(move)`), to account for a missing key. Here is an example:
2020-02-05 19:28:40 +04:00
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
```pascaligo group=big_maps
2020-02-05 19:28:40 +04:00
const my_balance : option (move) =
moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)]
```
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
2020-02-05 19:28:40 +04:00
```cameligo group=big_maps
2020-02-05 19:28:40 +04:00
let my_balance : move option =
Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
2020-02-05 19:28:40 +04:00
```reasonligo group=big_maps
2020-02-05 19:28:40 +04:00
let my_balance : option (move) =
Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, moves);
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
### Updating Big Maps
<!--DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-05 19:28:40 +04:00
The values of a PascaLIGO big map can be updated using the
assignment syntax for ordinary maps
```pascaligo group=big_maps
2020-02-10 22:07:20 +04:00
function add (var m : register) : register is
2020-02-05 19:28:40 +04:00
block {
m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9)
} with m
2020-02-10 22:07:20 +04:00
const updated_map : register = add (moves)
```
2020-02-10 22:07:20 +04:00
<!--CameLIGO-->
2020-02-05 19:28:40 +04:00
We can update a big map in CameLIGO using the `Big_map.update`
built-in:
```cameligo group=big_maps
2020-02-05 19:28:40 +04:00
let updated_map : register =
Big_map.update
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) moves
```
2020-02-10 22:07:20 +04:00
<!--ReasonLIGO-->
2020-02-05 19:28:40 +04:00
We can update a big map in ReasonLIGO using the `Big_map.update`
built-in:
```reasonligo group=big_maps
2020-02-05 19:28:40 +04:00
let updated_map : register =
Big_map.update
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some ((4,9)), moves);
2019-12-10 17:47:31 +04:00
```
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--END_DOCUSAURUS_CODE_TABS-->
### Removing Bindings
Removing a binding in a map is done differently according to the LIGO
syntax.
<!--DOCUSAURUS_CODE_TABS-->
<!--PascaLIGO-->
PascaLIGO features a special syntactic construct to remove bindings
from maps, of the form `remove <key> from map <map>`. For example,
```pascaligo group=big_maps
2020-02-10 22:07:20 +04:00
function rem (var m : register) : register is
block {
remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) from map moves
} with m
const updated_map : register = rem (moves)
```
<!--CameLIGO-->
In CameLIGO, the predefined function which removes a binding in a map
is called `Map.remove` and is used as follows:
```cameligo group=big_maps
2020-02-10 22:07:20 +04:00
let updated_map : register =
Map.remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
```
<!--ReasonLIGO-->
In ReasonLIGO, the predefined function which removes a binding in a map
is called `Map.remove` and is used as follows:
```reasonligo group=big_maps
2020-02-10 22:07:20 +04:00
let updated_map : register =
Map.remove (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves)
```
2020-02-05 19:28:40 +04:00
<!--END_DOCUSAURUS_CODE_TABS-->