Merge branch 'feature/doc-pascaligo-loop' into 'dev'

Some doc update for Ligo training

See merge request ligolang/ligo!364
This commit is contained in:
Christian Rinderknecht 2020-01-29 14:56:50 +00:00
commit 04381b9dcf
4 changed files with 265 additions and 165 deletions

View File

@ -5,63 +5,132 @@ title: Entrypoints, Contracts
## Entrypoints ## Entrypoints
Each LIGO smart contract is essentially a single function, that has the following *(pseudo)* type signature: Each LIGO smart contract is essentially a single main function, referring to the following types:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
``` ```pascaligo group=a
(const parameter: my_type, const store: my_store_type): (list(operation), my_store_type) type parameter_t is unit
type storage_t is unit
type return_t is (list(operation) * storage_t)
``` ```
<!--CameLIGO--> <!--CameLIGO-->
``` ```cameligo group=a
(parameter, store: my_type * my_store_type) : operation list * my_store_type type parameter_t = unit
type storage_t = unit
type return_t = (operation list * storage_t)
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo group=a
type parameter_t = unit;
type storage_t = unit;
type return_t = (list(operation) , storage_t);
``` ```
(parameter_store: (my_type, my_store_type)) : (list(operation), my_store_type)
```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
This means that every smart contract needs at least one entrypoint function, here's an example: Each main function receives two arguments:
- `parameter` - this is the parameter received in the invocation operation
- `storage` - this is the current (real) on-chain storage value
Storage can only be modified by running the smart contract entrypoint, which is responsible for returning a pair holding a list of operations, and a new storage.
Here is an example of a smart contract main function:
> 💡 The contract below literally does *nothing* > 💡 The contract below literally does *nothing*
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo group=a ```pascaligo group=a
type parameter is unit; function main(const parameter: parameter_t; const store: storage_t): return_t is
type store is unit; ((nil : list(operation)), store)
function main(const parameter: parameter; const store: store): (list(operation) * store) is
block { skip } with ((nil : list(operation)), store)
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo group=a ```cameligo group=a
type parameter = unit let main (parameter, store: parameter_t * storage_t) : return_t =
type store = unit
let main (parameter, store: parameter * store) : operation list * store =
(([]: operation list), store) (([]: operation list), store)
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo group=a ```reasonligo group=a
type parameter = unit; let main = ((parameter, store): (parameter_t, storage_t)) : return_t => {
type store = unit;
let main = ((parameter, store): (parameter, store)) : (list(operation), store) => {
(([]: list(operation)), store); (([]: list(operation)), store);
}; };
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
Each entrypoint function receives two arguments: A contract entrypoints are the constructors of the parameter type (variant) and you must use pattern matching (`case`, `match`, `switch`) on the parameter in order to associate each entrypoint to its corresponding handler.
- `parameter` - this is the parameter received in the invocation operation
- `storage` - this is the current (real) on-chain storage value
Storage can only be modified by running the smart contract entrypoint, which is responsible for returning a list of operations, and a new storage at the end of it's execution. To access the 'entrypoints' of a contract, we define a main function whose parameter is a variant type with constructors for each entrypoint. This allows us to satisfy the requirement that LIGO contracts always begin execution from the same function. The main function simply takes this variant, pattern matches it to determine which entrypoint to dispatch the call to, then returns the result of executing that entrypoint with the projected arguments.
> The LIGO variant's are compiled to a Michelson annotated tree of union type.
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
```pascaligo group=recordentry
type parameter_t is
| Entrypoint_a of int
| Entrypoint_b of string
type storage_t is unit
type return_t is (list(operation) * storage_t)
function handle_a (const p : int; const store : storage_t) : return_t is
((nil : list(operation)), store)
function handle_b (const p : string; const store : storage_t) : return_t is
((nil : list(operation)), store)
function main(const parameter: parameter_t; const store: storage_t): return_t is
case parameter of
| Entrypoint_a (p) -> handle_a(p,store)
| Entrypoint_b (p) -> handle_b(p,store)
end
```
<!--CameLIGO-->
```cameligo group=recordentry
type parameter_t =
| Entrypoint_a of int
| Entrypoint_b of string
type storage_t = unit
type return_t = (operation list * storage_t)
let handle_a (parameter, store: int * storage_t) : return_t =
(([]: operation list), store)
let handle_b (parameter, store: string * storage_t) : return_t =
(([]: operation list), store)
let main (parameter, store: parameter_t * storage_t) : return_t =
match parameter with
| Entrypoint_a p -> handle_a (p,store)
| Entrypoint_b p -> handle_b (p,store)
```
<!--ReasonLIGO-->
```reasonligo group=recordentry
type parameter_t =
| Entrypoint_a(int)
| Entrypoint_b(string);
type storage_t = unit;
type return_t = (list(operation) , storage_t);
let handle_a = ((parameter, store): (int, storage_t)) : return_t => {
(([]: list(operation)), store); };
let handle_b = ((parameter, store): (string, storage_t)) : return_t => {
(([]: list(operation)), store); };
let main = ((parameter, store): (parameter_t, storage_t)) : return_t => {
switch (parameter) {
| Entrypoint_a(p) => handle_a((p,store))
| Entrypoint_b(p) => handle_b((p,store))
}
};
```
<!--END_DOCUSAURUS_CODE_TABS-->
## Built-in contract variables ## Built-in contract variables

View File

@ -15,10 +15,10 @@ Each `block` needs to include at least one `instruction`, or a *placeholder* ins
```pascaligo skip ```pascaligo skip
// shorthand syntax // shorthand syntax
block { skip } block { a := a + 1 }
// verbose syntax // verbose syntax
begin begin
skip a := a + 1
end end
``` ```
@ -29,16 +29,17 @@ end
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
Functions in PascaLIGO are defined using the `function` keyword followed by their `name`, `parameters` and `return` type definitions. Functions in PascaLIGO are defined using the `function` keyword
followed by their `name`, `parameters` and `return` type definitions.
Here's how you define a basic function that accepts two `ints` and returns a single `int`:
Here's how you define a basic function that accepts two `int`s and
returns a single `int`:
```pascaligo group=a ```pascaligo group=a
function add(const a: int; const b: int): int is function add(const a: int; const b: int): int is
begin begin
const result: int = a + b; const result: int = a + b;
end with result; end with result;
``` ```
The function body consists of two parts: The function body consists of two parts:
@ -48,8 +49,10 @@ The function body consists of two parts:
#### Blockless functions #### Blockless functions
Functions that can contain all of their logic into a single instruction/expression, can be defined without the surrounding `block`. Functions that can contain all of their logic into a single
Instead, you can inline the necessary logic directly, like this: instruction/expression, can be defined without the surrounding
`block`. Instead, you can inline the necessary logic directly, like
this:
```pascaligo group=b ```pascaligo group=b
function add(const a: int; const b: int): int is a + b function add(const a: int; const b: int): int is a + b
@ -57,72 +60,78 @@ function add(const a: int; const b: int): int is a + b
<!--CameLIGO--> <!--CameLIGO-->
Functions in CameLIGO are defined using the `let` keyword, like value bindings. Functions in CameLIGO are defined using the `let` keyword, like value
The difference is that after the value name a list of function parameters is provided, bindings. The difference is that after the value name a list of
along with a return type. function parameters is provided, along with a return type.
CameLIGO is a little different from other syntaxes when it comes to function CameLIGO is a little different from other syntaxes when it comes to
parameters. In OCaml, functions can only take one parameter. To get functions function parameters. In OCaml, functions can only take one
with multiple arguments like we're used to in traditional programming languages, parameter. To get functions with multiple arguments like we are used
a technique called [currying](https://en.wikipedia.org/wiki/Currying) is used. to in traditional programming languages, a technique called
Currying essentially translates a function with multiple arguments into a series [currying](https://en.wikipedia.org/wiki/Currying) is used. Currying
of single argument functions, each returning a new function accepting the next essentially translates a function with multiple arguments into a
argument until every parameter is filled. This is useful because it means that series of single argument functions, each returning a new function
CameLIGO can support [partial application](https://en.wikipedia.org/wiki/Partial_application). accepting the next argument until every parameter is filled. This is
useful because it means that CameLIGO can support
[partial application](https://en.wikipedia.org/wiki/Partial_application).
Currying is however *not* the preferred way to pass function arguments in CameLIGO. Currying is however *not* the preferred way to pass function arguments
While this approach is faithful to the original OCaml, it's costlier in Michelson in CameLIGO. While this approach is faithful to the original OCaml,
than naive function execution accepting multiple arguments. Instead for most it's costlier in Michelson than naive function execution accepting
functions with more than one parameter we should place the arguments in a multiple arguments. Instead for most functions with more than one
[tuple](language-basics/sets-lists-tuples.md) and pass the tuple in as a single parameter we should place the arguments in a
parameter. [tuple](language-basics/sets-lists-tuples.md) and pass the tuple in as
a single parameter.
Here's how you define a basic function that accepts two `ints` and returns an `int` as well: Here is how you define a basic function that accepts two `ints` and
returns an `int` as well:
```cameligo group=b ```cameligo group=b
let add (a,b: int * int) : int = a + b let add (a,b: int * int) : int = a + b
let add_curry (a: int) (b: int) : int = a + b let add_curry (a: int) (b: int) : int = a + b
``` ```
The function body is a series of expressions, which are evaluated to give the return The function body is a series of expressions, which are evaluated to
value. give the return value.
<!--ReasonLIGO--> <!--ReasonLIGO-->
Functions in ReasonLIGO are defined using the `let` keyword, like value bindings. Functions in ReasonLIGO are defined using the `let` keyword, like
The difference is that after the value name a list of function parameters is provided, value bindings. The difference is that after the value name a list of
along with a return type. function parameters is provided, along with a return type.
Here's how you define a basic function that accepts two `ints` and returns an `int` as well: Here is how you define a basic function that accepts two `int`s and
returns an `int` as well:
```reasonligo group=b ```reasonligo group=b
let add = ((a,b): (int, int)) : int => a + b; let add = ((a,b): (int, int)) : int => a + b;
``` ```
The function body is a series of expressions, which are evaluated to give the return The function body is a series of expressions, which are evaluated to
value. give the return value.
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
## Anonymous functions ## Anonymous functions
Functions without a name, also known as anonymous functions are useful in cases when you want to pass the function as an argument or assign it to a key in a record/map. Functions without a name, also known as anonymous functions are useful
in cases when you want to pass the function as an argument or assign
it to a key in a record or a map.
Here's how to define an anonymous function assigned to a variable `increment`, with it's appropriate function type signature. Here's how to define an anonymous function assigned to a variable
`increment`, with it is appropriate function type signature.
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo group=c ```pascaligo group=c
const increment : (int -> int) = (function (const i : int) : int is i + 1); const increment : int -> int = function (const i : int) : int is i + 1;
// a = 2 // a = 2
const a: int = increment(1); const a: int = increment (1);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo group=c ```cameligo group=c
let increment : (int -> int) = fun (i: int) -> i + 1 let increment : int -> int = fun (i: int) -> i + 1
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->

View File

@ -3,19 +3,22 @@ id: maps-records
title: Maps, Records title: Maps, Records
--- ---
So far we've seen pretty basic data types. LIGO also offers more complex built-in constructs, such as Maps and Records. So far we have seen pretty basic data types. LIGO also offers more
complex built-in constructs, such as maps and records.
## Maps ## Maps
Maps are natively available in Michelson, and LIGO builds on top of them. A requirement for a Map is that its keys be of the same type, and that type must be comparable. Maps are natively available in Michelson, and LIGO builds on top of
them. A requirement for a map is that its keys be of the same type,
and that type must be comparable.
Here's how a custom map type is defined: Here is how a custom map type is defined:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo
type move is (int * int); type move is int * int
type moveset is map(address, move); type moveset is map(address, move)
``` ```
<!--CameLIGO--> <!--CameLIGO-->
@ -32,7 +35,7 @@ type moveset = map(address, move);
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
And here's how a map value is populated: And here is how a map value is populated:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
@ -77,7 +80,10 @@ let moves : moveset =
### Accessing map values by key ### 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: 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 will get will be wrapped as an optional; in our case
`option(move)`. Here is an example:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
@ -175,8 +181,8 @@ otherwise.
function iter_op (const m : moveset) : unit is function iter_op (const m : moveset) : unit is
block { block {
function aggregate (const i : address ; const j : move) : unit is block function aggregate (const i : address ; const j : move) : unit is block
{ if (j.1 > 1) then skip else failwith("fail") } with unit ; { if j.1 > 1 then skip else failwith("fail") } with unit
} with map_iter(aggregate, m) ; } with map_iter(aggregate, m);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
@ -189,7 +195,7 @@ let iter_op (m : moveset) : unit =
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
let iter_op = (m: moveset): unit => { let iter_op = (m: moveset): unit => {
let assert_eq = ((i,j): (address, move)) => assert(j[0] > 1); let assert_eq = ((i,j): (address, move)) => assert (j[0] > 1);
Map.iter(assert_eq, m); Map.iter(assert_eq, m);
}; };
``` ```
@ -202,8 +208,8 @@ let iter_op = (m: moveset): unit => {
```pascaligo ```pascaligo
function map_op (const m : moveset) : moveset is function map_op (const m : moveset) : moveset is
block { block {
function increment (const i : address ; const j : move) : move is block { skip } with (j.0, j.1 + 1) ; function increment (const i : address ; const j : move) : move is (j.0, j.1 + 1);
} with map_map(increment, m) ; } with map_map (increment, m);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
@ -222,29 +228,30 @@ let map_op = (m: moveset): moveset => {
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
`fold` is an aggregation function that return the combination of a maps contents. `fold` is an aggregation function that return the combination of a
maps contents.
The fold is a loop which extracts an element of the map on each iteration. It then The fold is a loop which extracts an element of the map on each
provides this element and an existing value to a folding function which combines them. iteration. It then provides this element and an existing value to a
On the first iteration, the existing value is an initial expression given by the folding function which combines them. On the first iteration, the
programmer. On each subsequent iteration it is the result of the previous iteration. existing value is an initial expression given by the programmer. On
each subsequent iteration it is the result of the previous iteration.
It eventually returns the result of combining all the elements. It eventually returns the result of combining all the elements.
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo
function fold_op (const m : moveset) : int is function fold_op (const m : moveset) : int is
block { block {
function aggregate (const j : int ; const cur : (address * (int * int))) : int is j + cur.1.1 ; function aggregate (const j : int; const cur : address * (int * int)) : int is j + cur.1.1
} with map_fold(aggregate, m , 5) } with map_fold(aggregate, m, 5)
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo
let fold_op (m : moveset) : moveset = let fold_op (m : moveset) : moveset =
let aggregate = fun (i,j: int * (address * (int * int))) -> i + j.1.1 in let aggregate = fun (i,j: int * (address * (int * int))) -> i + j.1.1
Map.fold aggregate m 5 in Map.fold aggregate m 5
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
@ -268,13 +275,13 @@ 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 Tezos which handles the scaling concerns for us. In LIGO, the interface for big
maps is analogous to the one used for ordinary maps. maps is analogous to the one used for ordinary maps.
Here's how we define a big map: Here is how we define a big map:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo
type move is (int * int); type move is (int * int)
type moveset is big_map(address, move); type moveset is big_map (address, move)
``` ```
<!--CameLIGO--> <!--CameLIGO-->
@ -291,16 +298,17 @@ type moveset = big_map(address, move);
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
And here's how a map value is populated: And here is how a map value is populated:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo
const moves: moveset = big_map const moves: moveset =
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1, 2); big_map
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0, 3); ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1,2);
end ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0,3);
end
``` ```
> Notice the `->` between the key and its value and `;` to separate individual map entries. > Notice the `->` between the key and its value and `;` to separate individual map entries.
> >
@ -309,45 +317,51 @@ end
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo
let moves: moveset = Big_map.literal let moves: moveset =
[ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1, 2)) ; Big_map.literal [
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0, 3)) ; (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1,2));
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0,3));
] ]
``` ```
> Big_map.literal constructs the map from a list of key-value pair tuples, `(<key>, <value>)`. > Big_map.literal constructs the map from a list of key-value pair tuples, `(<key>, <value>)`.
> Note also the `;` to separate individual map entries. > Note also the `;` to separate individual map entries.
> >
> `("<string value>": address)` means that we type-cast a string into an address. > `("<string value>": address)` means that we cast a string into an address.
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
let moves: moveset = let moves: moveset =
Big_map.literal([ Big_map.literal ([
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1, 2)), ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1,2)),
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0, 3)), ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0,3)),
]); ]);
``` ```
> Big_map.literal constructs the map from a list of key-value pair tuples, `(<key>, <value>)`. > Big_map.literal constructs the map from a list of key-value pair tuples, `(<key>, <value>)`.
> >
> `("<string value>": address)` means that we type-cast a string into an address. > `("<string value>": address)` means that we cast a string into an address.
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
### Accessing map values by key ### 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: 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 will get will be wrapped as an optional; in our case
`option(move)`. Here is an example:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo
const my_balance : option(move) = moves[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]; const my_balance : option(move) =
moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo
let my_balance : move option = Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves let my_balance : move option =
Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
@ -360,24 +374,28 @@ let my_balance : option(move) =
#### Obtaining a map value forcefully #### Obtaining a map value forcefully
Accessing a value in a map yields an option, however you can also get the value directly: Accessing a value in a map yields an option, however you can also get
the value directly:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo
const my_balance : move = get_force(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves); const my_balance : move =
get_force (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo
let my_balance : move = Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves let my_balance : move =
Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
let my_balance : move = Big_map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); let my_balance : move =
Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves);
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -388,19 +406,21 @@ let my_balance : move = Big_map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": add
<!--Pascaligo--> <!--Pascaligo-->
The values of a PascaLIGO big map can be updated using the ordinary assignment syntax: The values of a PascaLIGO big map can be updated using the ordinary
assignment syntax:
```pascaligo ```pascaligo
function set_ (var m : moveset) : moveset is function set_ (var m : moveset) : moveset is
block { block {
m[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9); m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9);
} with m } with m
``` ```
<!--Cameligo--> <!--Cameligo-->
We can update a big map in CameLIGO using the `Big_map.update` built-in: We can update a big map in CameLIGO using the `Big_map.update`
built-in:
```cameligo ```cameligo
@ -410,10 +430,11 @@ let updated_map : moveset =
<!--Reasonligo--> <!--Reasonligo-->
We can update a big map in ReasonLIGO using the `Big_map.update` built-in: We can update a big map in ReasonLIGO using the `Big_map.update`
built-in:
```reasonligo ```reasonligo
let updated_map: moveset = let updated_map : moveset =
Big_map.update(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); Big_map.update(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves);
``` ```
@ -421,75 +442,79 @@ let updated_map: moveset =
## Records ## Records
Records are a construct introduced in LIGO, and are not natively available in Michelson. The LIGO compiler translates records into Michelson `Pairs`. Records are a construct introduced in LIGO, and are not natively
available in Michelson. The LIGO compiler translates records into
Michelson `Pairs`.
Here's how a custom record type is defined: Here is how a custom record type is defined:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo
type user is record type user is
id: nat; record
is_admin: bool; id : nat;
name: string; is_admin : bool;
end name : string
end
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo
type user = { type user = {
id: nat; id : nat;
is_admin: bool; is_admin : bool;
name: string; name : string
} }
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
type user = { type user = {
id: nat, id : nat,
is_admin: bool, is_admin : bool,
name: string name : string
}; };
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
And here's how a record value is populated: And here is how a record value is populated:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo
const user: user = record const user : user =
id = 1n; record
id = 1n;
is_admin = True; is_admin = True;
name = "Alice"; name = "Alice"
end end
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo
let user: user = { let user : user = {
id = 1n; id = 1n;
is_admin = true; is_admin = true;
name = "Alice"; name = "Alice"
} }
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
let user: user = { let user : user = {
id: 1n, id : 1n,
is_admin: true, is_admin : true,
name: "Alice" name : "Alice"
}; };
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
### Accessing record keys by name ### Accessing record keys by name
If we want to obtain a value from a record for a given key, we can do the following: If we want to obtain a value from a record for a given key, we can do
the following:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
@ -506,5 +531,4 @@ let is_admin : bool = user.is_admin
```reasonligo ```reasonligo
let is_admin: bool = user.is_admin; let is_admin: bool = user.is_admin;
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->

View File

@ -3,39 +3,37 @@ id: sets-lists-tuples
title: Sets, Lists, Tuples title: Sets, Lists, Tuples
--- ---
Apart from complex data types such as `maps` and `records`, ligo also exposes `sets`, `lists` and `tuples`. Apart from complex data types such as `maps` and `records`, ligo also
exposes `sets`, `lists` and `tuples`.
> ⚠️ Make sure to pick the appropriate data type for your use case; it carries not only semantic but also gas related costs. > ⚠️ Make sure to pick the appropriate data type for your use case; it carries not only semantic but also gas related costs.
## Sets ## Sets
Sets are similar to lists. The main difference is that elements of a `set` must be *unique*. Sets are similar to lists. The main difference is that elements of a
`set` must be *unique*.
### Defining a set ### Defining a set
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo group=a ```pascaligo group=a
type int_set is set(int); type int_set is set (int);
const my_set: int_set = set const my_set : int_set = set 1; 2; 3 end
1;
2;
3;
end
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo group=a ```cameligo group=a
type int_set = int set type int_set = int set
let my_set: int_set = let my_set : int_set =
Set.add 3 (Set.add 2 (Set.add 1 (Set.empty: int set))) Set.add 3 (Set.add 2 (Set.add 1 (Set.empty: int set)))
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo group=a ```reasonligo group=a
type int_set = set(int); type int_set = set (int);
let my_set: int_set = let my_set : int_set =
Set.add(3, Set.add(2, Set.add(1, Set.empty: set(int)))); Set.add (3, Set.add (2, Set.add (1, Set.empty: set (int))));
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -45,8 +43,8 @@ let my_set: int_set =
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo group=a ```pascaligo group=a
const my_set: int_set = set end; const my_set: int_set = set end
const my_set_2: int_set = set_empty; const my_set_2: int_set = set_empty
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo group=a ```cameligo group=a
@ -54,7 +52,7 @@ let my_set: int_set = (Set.empty: int set)
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo group=a ```reasonligo group=a
let my_set: int_set = (Set.empty: set(int)); let my_set: int_set = (Set.empty: set (int));
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -63,9 +61,9 @@ let my_set: int_set = (Set.empty: set(int));
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo group=a ```pascaligo group=a
const contains_three: bool = my_set contains 3; const contains_three : bool = my_set contains 3
// or alternatively // or alternatively
const contains_three_fn: bool = set_mem(3, my_set); const contains_three_fn: bool = set_mem (3, my_set);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
@ -84,7 +82,7 @@ let contains_three: bool = Set.mem(3, my_set);
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo group=a ```pascaligo group=a
const set_size: nat = size(my_set); const set_size: nat = size (my_set)
``` ```
<!--CameLIGO--> <!--CameLIGO-->
@ -94,7 +92,7 @@ let set_size: nat = Set.size my_set
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo group=a ```reasonligo group=a
let set_size: nat = Set.size(my_set); let set_size: nat = Set.size (my_set);
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->