This commit is contained in:
Christian Rinderknecht 2020-01-28 15:13:50 +01:00
parent 334deea8ec
commit 729575af11
3 changed files with 172 additions and 141 deletions

View File

@ -15,10 +15,10 @@ Each `block` needs to include at least one `instruction`, or a *placeholder* ins
```pascaligo skip
// shorthand syntax
block { skip }
block { a := a + 1 }
// verbose syntax
begin
skip
a := a + 1
end
```
@ -29,16 +29,17 @@ end
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
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`:
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 `int`s and
returns a single `int`:
```pascaligo group=a
function add(const a: int; const b: int): int is
begin
const result: int = a + b;
end with result;
function add(const a: int; const b: int): int is
begin
const result: int = a + b;
end with result;
```
The function body consists of two parts:
@ -48,8 +49,10 @@ The function body consists of two parts:
#### Blockless functions
Functions that can contain all of their logic into a single instruction/expression, can be defined without the surrounding `block`.
Instead, you can inline the necessary logic directly, like this:
Functions that can contain all of their logic into a single
instruction/expression, can be defined without the surrounding
`block`. Instead, you can inline the necessary logic directly, like
this:
```pascaligo group=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-->
Functions in CameLIGO are defined using the `let` keyword, like value bindings.
The difference is that after the value name a list of function parameters is provided,
along with a return type.
Functions in CameLIGO are defined using the `let` keyword, like value
bindings. The difference is that after the value name a list of
function parameters is provided, along with a return type.
CameLIGO is a little different from other syntaxes when it comes to function
parameters. In OCaml, functions can only take one parameter. To get functions
with multiple arguments like we're used to in traditional programming languages,
a technique called [currying](https://en.wikipedia.org/wiki/Currying) is used.
Currying essentially translates a function with multiple arguments into a series
of single argument functions, each returning a new function 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).
CameLIGO is a little different from other syntaxes when it comes to
function parameters. In OCaml, functions can only take one
parameter. To get functions with multiple arguments like we are used
to in traditional programming languages, a technique called
[currying](https://en.wikipedia.org/wiki/Currying) is used. Currying
essentially translates a function with multiple arguments into a
series of single argument functions, each returning a new function
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.
While this approach is faithful to the original OCaml, it's costlier in Michelson
than naive function execution accepting multiple arguments. Instead for most
functions with more than one parameter we should place the arguments in a
[tuple](language-basics/sets-lists-tuples.md) and pass the tuple in as a single
parameter.
Currying is however *not* the preferred way to pass function arguments
in CameLIGO. While this approach is faithful to the original OCaml,
it's costlier in Michelson than naive function execution accepting
multiple arguments. Instead for most functions with more than one
parameter we should place the arguments in a
[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
let add (a,b: int * 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
value.
The function body is a series of expressions, which are evaluated to
give the return value.
<!--ReasonLIGO-->
Functions in ReasonLIGO are defined using the `let` keyword, like value bindings.
The difference is that after the value name a list of function parameters is provided,
along with a return type.
Functions in ReasonLIGO are defined using the `let` keyword, like
value bindings. The difference is that after the value name a list of
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
let add = ((a,b): (int, int)) : int => a + b;
```
The function body is a series of expressions, which are evaluated to give the return
value.
The function body is a series of expressions, which are evaluated to
give the return value.
<!--END_DOCUSAURUS_CODE_TABS-->
## 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-->
<!--Pascaligo-->
```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
const a: int = increment(1);
const a: int = increment (1);
```
<!--CameLIGO-->
```cameligo group=c
let increment : (int -> int) = fun (i: int) -> i + 1
let increment : int -> int = fun (i: int) -> i + 1
```
<!--ReasonLIGO-->

View File

@ -3,19 +3,22 @@ id: 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 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-->
<!--Pascaligo-->
```pascaligo
type move is (int * int);
type moveset is map(address, move);
type move is int * int
type moveset is map(address, move)
```
<!--CameLIGO-->
@ -32,7 +35,7 @@ type moveset = map(address, move);
<!--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-->
<!--Pascaligo-->
@ -77,7 +80,10 @@ let moves : moveset =
### 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-->
<!--Pascaligo-->
@ -175,8 +181,8 @@ otherwise.
function iter_op (const m : moveset) : 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 ;
} with map_iter(aggregate, m) ;
{ if j.1 > 1 then skip else failwith("fail") } with unit
} with map_iter(aggregate, m);
```
<!--CameLIGO-->
@ -189,7 +195,7 @@ let iter_op (m : moveset) : unit =
<!--ReasonLIGO-->
```reasonligo
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);
};
```
@ -202,8 +208,8 @@ let iter_op = (m: moveset): unit => {
```pascaligo
function map_op (const m : moveset) : moveset is
block {
function increment (const i : address ; const j : move) : move is block { skip } with (j.0, j.1 + 1) ;
} with map_map(increment, m) ;
function increment (const i : address ; const j : move) : move is (j.0, j.1 + 1);
} with map_map (increment, m);
```
<!--CameLIGO-->
@ -222,29 +228,30 @@ let map_op = (m: moveset): moveset => {
```
<!--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
provides this element and an existing value to a folding function which combines them.
On the first iteration, the existing value is an initial expression given by the
programmer. On each subsequent iteration it is the result of the previous iteration.
The fold is a loop which extracts an element of the map on each
iteration. It then provides this element and an existing value to a
folding function which combines them. On the first iteration, the
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.
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
```pascaligo
function fold_op (const m : moveset) : int is
block {
function aggregate (const j : int ; const cur : (address * (int * int))) : int is j + cur.1.1 ;
} with map_fold(aggregate, m , 5)
function aggregate (const j : int; const cur : address * (int * int)) : int is j + cur.1.1
} with map_fold(aggregate, m, 5)
```
<!--CameLIGO-->
```cameligo
let fold_op (m : moveset) : moveset =
let aggregate = fun (i,j: int * (address * (int * int))) -> i + j.1.1 in
Map.fold aggregate m 5
let aggregate = fun (i,j: int * (address * (int * int))) -> i + j.1.1
in Map.fold aggregate m 5
```
<!--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
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-->
<!--Pascaligo-->
```pascaligo
type move is (int * int);
type moveset is big_map(address, move);
type move is (int * int)
type moveset is big_map (address, move)
```
<!--CameLIGO-->
@ -291,16 +298,17 @@ type moveset = big_map(address, move);
<!--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-->
<!--Pascaligo-->
```pascaligo
const moves: moveset = big_map
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1, 2);
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0, 3);
end
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.
>
@ -309,45 +317,51 @@ end
<!--CameLIGO-->
```cameligo
let moves: moveset = Big_map.literal
[ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1, 2)) ;
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0, 3)) ;
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, `(<key>, <value>)`.
> 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
let moves: moveset =
Big_map.literal([
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1, 2)),
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0, 3)),
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, `(<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-->
### 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-->
<!--Pascaligo-->
```pascaligo
const my_balance : option(move) = moves[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)];
const my_balance : option(move) =
moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]
```
<!--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-->
@ -360,24 +374,28 @@ let my_balance : option(move) =
#### 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-->
<!--Pascaligo-->
```pascaligo
const my_balance : move = get_force(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves);
const my_balance : move =
get_force (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves);
```
<!--CameLIGO-->
```cameligo
let my_balance : move = Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
let my_balance : move =
Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
```
<!--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-->
@ -388,19 +406,21 @@ let my_balance : move = Big_map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": add
<!--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
function set_ (var m : moveset) : moveset is
block {
m[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9);
m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9);
} with m
```
<!--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
@ -410,10 +430,11 @@ let updated_map : moveset =
<!--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
let updated_map: moveset =
let updated_map : moveset =
Big_map.update(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves);
```
@ -421,75 +442,79 @@ let updated_map: moveset =
## 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-->
<!--Pascaligo-->
```pascaligo
type user is record
id: nat;
is_admin: bool;
name: string;
end
type user is
record
id : nat;
is_admin : bool;
name : string
end
```
<!--CameLIGO-->
```cameligo
type user = {
id: nat;
is_admin: bool;
name: string;
id : nat;
is_admin : bool;
name : string
}
```
<!--ReasonLIGO-->
```reasonligo
type user = {
id: nat,
is_admin: bool,
name: string
id : nat,
is_admin : bool,
name : string
};
```
<!--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-->
<!--Pascaligo-->
```pascaligo
const user: user = record
id = 1n;
const user : user =
record
id = 1n;
is_admin = True;
name = "Alice";
end
name = "Alice"
end
```
<!--CameLIGO-->
```cameligo
let user: user = {
id = 1n;
let user : user = {
id = 1n;
is_admin = true;
name = "Alice";
name = "Alice"
}
```
<!--ReasonLIGO-->
```reasonligo
let user: user = {
id: 1n,
is_admin: true,
name: "Alice"
let user : user = {
id : 1n,
is_admin : true,
name : "Alice"
};
```
<!--END_DOCUSAURUS_CODE_TABS-->
### 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-->
<!--Pascaligo-->
@ -506,5 +531,4 @@ let is_admin : bool = user.is_admin
```reasonligo
let is_admin: bool = user.is_admin;
```
<!--END_DOCUSAURUS_CODE_TABS-->

View File

@ -3,39 +3,37 @@ id: 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.
## 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
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
```pascaligo group=a
type int_set is set(int);
const my_set: int_set = set
1;
2;
3;
end
type int_set is set (int);
const my_set : int_set = set 1; 2; 3 end
```
<!--CameLIGO-->
```cameligo group=a
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)))
```
<!--ReasonLIGO-->
```reasonligo group=a
type int_set = set(int);
let my_set: int_set =
Set.add(3, Set.add(2, Set.add(1, Set.empty: set(int))));
type int_set = set (int);
let my_set : int_set =
Set.add (3, Set.add (2, Set.add (1, Set.empty: set (int))));
```
<!--END_DOCUSAURUS_CODE_TABS-->
@ -45,8 +43,8 @@ let my_set: int_set =
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
```pascaligo group=a
const my_set: int_set = set end;
const my_set_2: int_set = set_empty;
const my_set: int_set = set end
const my_set_2: int_set = set_empty
```
<!--CameLIGO-->
```cameligo group=a
@ -54,7 +52,7 @@ let my_set: int_set = (Set.empty: int set)
```
<!--ReasonLIGO-->
```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-->
@ -63,9 +61,9 @@ let my_set: int_set = (Set.empty: set(int));
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
```pascaligo group=a
const contains_three: bool = my_set contains 3;
const contains_three : bool = my_set contains 3
// or alternatively
const contains_three_fn: bool = set_mem(3, my_set);
const contains_three_fn: bool = set_mem (3, my_set);
```
<!--CameLIGO-->
@ -84,7 +82,7 @@ let contains_three: bool = Set.mem(3, my_set);
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
```pascaligo group=a
const set_size: nat = size(my_set);
const set_size: nat = size (my_set)
```
<!--CameLIGO-->
@ -94,7 +92,7 @@ let set_size: nat = Set.size my_set
<!--ReasonLIGO-->
```reasonligo group=a
let set_size: nat = Set.size(my_set);
let set_size: nat = Set.size (my_set);
```
<!--END_DOCUSAURUS_CODE_TABS-->