From 729575af112f87b4623623a00a4e254cf6a71c01 Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Tue, 28 Jan 2020 15:13:50 +0100 Subject: [PATCH 1/9] Updates. --- .../docs/language-basics/functions.md | 97 +++++----- .../docs/language-basics/maps-records.md | 180 ++++++++++-------- .../docs/language-basics/sets-lists-tuples.md | 36 ++-- 3 files changed, 172 insertions(+), 141 deletions(-) diff --git a/gitlab-pages/docs/language-basics/functions.md b/gitlab-pages/docs/language-basics/functions.md index 5cf99f761..b8a839817 100644 --- a/gitlab-pages/docs/language-basics/functions.md +++ b/gitlab-pages/docs/language-basics/functions.md @@ -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 -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 -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. -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. ## 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. ```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 group=c -let increment : (int -> int) = fun (i: int) -> i + 1 +let increment : int -> int = fun (i: int) -> i + 1 ``` diff --git a/gitlab-pages/docs/language-basics/maps-records.md b/gitlab-pages/docs/language-basics/maps-records.md index 5f51de96c..3d05d41da 100644 --- a/gitlab-pages/docs/language-basics/maps-records.md +++ b/gitlab-pages/docs/language-basics/maps-records.md @@ -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: ```pascaligo -type move is (int * int); -type moveset is map(address, move); +type move is int * int +type moveset is map(address, move) ``` @@ -32,7 +35,7 @@ type moveset = map(address, move); -And here's how a map value is populated: +And here is how a map value is populated: @@ -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: @@ -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); ``` @@ -189,7 +195,7 @@ let iter_op (m : moveset) : unit = ```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); ``` @@ -222,29 +228,30 @@ let map_op = (m: moveset): moveset => { ``` -`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. - ```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 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 ``` @@ -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: ```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) ``` @@ -291,16 +298,17 @@ type moveset = big_map(address, move); -And here's how a map value is populated: +And here is how a map value is populated: ```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 -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, `(, )`. > Note also the `;` to separate individual map entries. > -> `("": address)` means that we type-cast a string into an address. +> `("": address)` means that we cast a string into an address. ```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, `(, )`. > -> `("": address)` means that we type-cast a string into an address. +> `("": address)` means that we 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: +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: ```pascaligo -const my_balance : option(move) = moves[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]; +const my_balance : option(move) = + moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] ``` ```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 ``` @@ -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: ```pascaligo -const my_balance : move = get_force(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves); +const my_balance : move = + get_force (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves); ``` ```cameligo -let my_balance : move = Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves +let my_balance : move = + Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves ``` ```reasonligo -let my_balance : move = Big_map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); +let my_balance : move = + Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); ``` @@ -388,19 +406,21 @@ let my_balance : move = Big_map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": add -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 ``` -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 = -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: ```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 type user = { - id: nat; - is_admin: bool; - name: string; + id : nat; + is_admin : bool; + name : string } ``` ```reasonligo type user = { - id: nat, - is_admin: bool, - name: string + id : nat, + is_admin : bool, + name : string }; ``` -And here's how a record value is populated: +And here is how a record value is populated: ```pascaligo -const user: user = record - id = 1n; +const user : user = + record + id = 1n; is_admin = True; - name = "Alice"; -end + name = "Alice" + end ``` ```cameligo -let user: user = { - id = 1n; +let user : user = { + id = 1n; is_admin = true; - name = "Alice"; + name = "Alice" } ``` ```reasonligo -let user: user = { - id: 1n, - is_admin: true, - name: "Alice" +let user : user = { + id : 1n, + is_admin : true, + name : "Alice" }; ``` - ### 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: @@ -506,5 +531,4 @@ let is_admin : bool = user.is_admin ```reasonligo let is_admin: bool = user.is_admin; ``` - diff --git a/gitlab-pages/docs/language-basics/sets-lists-tuples.md b/gitlab-pages/docs/language-basics/sets-lists-tuples.md index f423caf7d..bc05f0ce3 100644 --- a/gitlab-pages/docs/language-basics/sets-lists-tuples.md +++ b/gitlab-pages/docs/language-basics/sets-lists-tuples.md @@ -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 ```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 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 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)))); ``` @@ -45,8 +43,8 @@ let my_set: int_set = ```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 group=a @@ -54,7 +52,7 @@ let my_set: int_set = (Set.empty: int set) ``` ```reasonligo group=a -let my_set: int_set = (Set.empty: set(int)); +let my_set: int_set = (Set.empty: set (int)); ``` @@ -63,9 +61,9 @@ let my_set: int_set = (Set.empty: set(int)); ```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); ``` @@ -84,7 +82,7 @@ let contains_three: bool = Set.mem(3, my_set); ```pascaligo group=a -const set_size: nat = size(my_set); +const set_size: nat = size (my_set) ``` @@ -94,7 +92,7 @@ let set_size: nat = Set.size my_set ```reasonligo group=a -let set_size: nat = Set.size(my_set); +let set_size: nat = Set.size (my_set); ``` From fa9372c595618bd8fd50ce109874806f60ef8d5b Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Fri, 31 Jan 2020 12:24:25 +0100 Subject: [PATCH 2/9] Updated ParErr.ml for ReasonLIGO (may have caused Not_found). --- src/passes/1-parser/reasonligo/ParErr.ml | 270 ++++++++++++----------- 1 file changed, 143 insertions(+), 127 deletions(-) diff --git a/src/passes/1-parser/reasonligo/ParErr.ml b/src/passes/1-parser/reasonligo/ParErr.ml index 18b32b373..693c26a37 100644 --- a/src/passes/1-parser/reasonligo/ParErr.ml +++ b/src/passes/1-parser/reasonligo/ParErr.ml @@ -46,7 +46,7 @@ let message = "\n" | 11 -> "\n" - | 528 -> + | 544 -> "\n" | 61 -> "\n" @@ -72,7 +72,7 @@ let message = "\n" | 70 -> "\n" - | 524 -> + | 540 -> "\n" | 185 -> "\n" @@ -162,11 +162,11 @@ let message = "\n" | 59 -> "\n" - | 531 -> + | 547 -> "\n" | 225 -> "\n" - | 533 -> + | 549 -> "\n" | 223 -> "\n" @@ -232,11 +232,11 @@ let message = "\n" | 72 -> "\n" - | 483 -> + | 499 -> "\n" - | 484 -> + | 500 -> "\n" - | 423 -> + | 427 -> "\n" | 161 -> "\n" @@ -244,61 +244,61 @@ let message = "\n" | 160 -> "\n" - | 486 -> + | 502 -> "\n" - | 487 -> + | 503 -> "\n" - | 504 -> + | 520 -> + "\n" + | 529 -> + "\n" + | 514 -> + "\n" + | 515 -> "\n" | 513 -> "\n" - | 498 -> + | 504 -> "\n" - | 499 -> + | 505 -> "\n" - | 497 -> + | 506 -> "\n" - | 488 -> - "\n" - | 489 -> - "\n" - | 490 -> - "\n" - | 492 -> - "\n" - | 493 -> - "\n" - | 494 -> - "\n" - | 495 -> + | 508 -> "\n" | 509 -> "\n" | 510 -> "\n" - | 491 -> + | 511 -> "\n" - | 520 -> + | 525 -> "\n" - | 518 -> + | 526 -> "\n" - | 485 -> + | 507 -> + "\n" + | 536 -> + "\n" + | 534 -> + "\n" + | 501 -> + "\n" + | 376 -> + "\n" + | 370 -> + "\n" + | 371 -> + "\n" + | 373 -> "\n" | 372 -> "\n" - | 366 -> - "\n" - | 367 -> - "\n" | 369 -> "\n" - | 368 -> - "\n" - | 365 -> - "\n" | 76 -> "\n" - | 446 -> + | 450 -> "\n" | 326 -> "\n" @@ -328,61 +328,61 @@ let message = "\n" | 82 -> "\n" - | 448 -> - "\n" - | 449 -> - "\n" - | 451 -> - "\n" | 452 -> "\n" + | 453 -> + "\n" + | 455 -> + "\n" + | 456 -> + "\n" | 200 -> "\n" | 236 -> "\n" | 79 -> "\n" - | 467 -> + | 483 -> "\n" - | 468 -> + | 484 -> + "\n" + | 492 -> + "\n" + | 493 -> + "\n" + | 495 -> + "\n" + | 496 -> + "\n" + | 485 -> + "\n" + | 486 -> + "\n" + | 81 -> "\n" | 476 -> "\n" | 477 -> "\n" - | 479 -> - "\n" - | 480 -> - "\n" - | 469 -> - "\n" - | 470 -> - "\n" - | 81 -> - "\n" - | 460 -> - "\n" | 461 -> "\n" - | 455 -> - "\n" - | 454 -> - "\n" | 458 -> "\n" - | 348 -> + | 464 -> "\n" - | 356 -> + | 465 -> "\n" - | 360 -> + | 470 -> "\n" - | 359 -> + | 474 -> "\n" - | 355 -> + | 473 -> "\n" - | 349 -> + | 469 -> "\n" - | 457 -> + | 459 -> + "\n" + | 463 -> "\n" | 340 -> "\n" @@ -392,6 +392,22 @@ let message = "\n" | 347 -> "\n" + | 348 -> + "\n" + | 349 -> + "\n" + | 350 -> + "\n" + | 360 -> + "\n" + | 364 -> + "\n" + | 363 -> + "\n" + | 357 -> + "\n" + | 359 -> + "\n" | 342 -> "\n" | 343 -> @@ -408,93 +424,93 @@ let message = "\n" | 324 -> "\n" - | 389 -> - "\n" - | 436 -> - "\n" - | 437 -> - "\n" - | 438 -> - "\n" - | 439 -> + | 393 -> "\n" | 440 -> "\n" | 441 -> "\n" - | 435 -> + | 442 -> + "\n" + | 443 -> + "\n" + | 444 -> + "\n" + | 445 -> + "\n" + | 439 -> "\n" | 325 -> "\n" - | 362 -> + | 366 -> "\n" - | 363 -> - "\n" - | 373 -> - "\n" - | 374 -> - "\n" - | 413 -> - "\n" - | 420 -> - "\n" - | 408 -> - "\n" - | 409 -> - "\n" - | 407 -> - "\n" - | 375 -> - "\n" - | 376 -> + | 367 -> "\n" | 377 -> "\n" - | 402 -> - "\n" - | 403 -> - "\n" - | 404 -> - "\n" - | 405 -> + | 378 -> "\n" | 417 -> "\n" - | 418 -> + | 424 -> "\n" - | 401 -> + | 412 -> "\n" - | 429 -> + | 413 -> "\n" - | 427 -> - "\n" - | 364 -> - "\n" - | 384 -> - "\n" - | 385 -> - "\n" - | 383 -> - "\n" - | 378 -> + | 411 -> "\n" | 379 -> "\n" | 380 -> "\n" - | 394 -> + | 381 -> "\n" - | 395 -> + | 406 -> "\n" - | 396 -> + | 407 -> "\n" - | 397 -> + | 408 -> "\n" - | 399 -> + | 409 -> + "\n" + | 421 -> + "\n" + | 422 -> + "\n" + | 405 -> + "\n" + | 433 -> + "\n" + | 431 -> + "\n" + | 368 -> + "\n" + | 388 -> + "\n" + | 389 -> + "\n" + | 387 -> + "\n" + | 382 -> + "\n" + | 383 -> + "\n" + | 384 -> "\n" | 398 -> "\n" - | 393 -> + | 399 -> + "\n" + | 400 -> + "\n" + | 401 -> + "\n" + | 403 -> + "\n" + | 402 -> + "\n" + | 397 -> "\n" | 320 -> "\n" From a9b3d295fd60f258a00cb197524361dac9b8db9a Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Fri, 31 Jan 2020 12:31:25 +0100 Subject: [PATCH 3/9] Fixes to the lexer and the parser API. * The parameter for logging the lexer is now mandatory. * The ParserAPI now thread the logging of the lexer. * LexerMain.ml now call the logging of the lexers (CameLIGO, ReasonLIGO). * Fixed bug in lexer when a line comment ends with EOF. --- src/passes/1-parser/cameligo/LexerMain.ml | 5 ++++ src/passes/1-parser/pascaligo/LexerMain.ml | 4 +-- src/passes/1-parser/reasonligo/LexerMain.ml | 5 ++++ src/passes/1-parser/shared/Lexer.mli | 2 +- src/passes/1-parser/shared/Lexer.mll | 15 +++++----- src/passes/1-parser/shared/ParserAPI.ml | 32 ++++++++++++++------- src/passes/1-parser/shared/ParserAPI.mli | 9 +++++- src/passes/1-parser/shared/ParserUnit.ml | 2 +- 8 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/passes/1-parser/cameligo/LexerMain.ml b/src/passes/1-parser/cameligo/LexerMain.ml index e9775b803..6c0729851 100644 --- a/src/passes/1-parser/cameligo/LexerMain.ml +++ b/src/passes/1-parser/cameligo/LexerMain.ml @@ -7,3 +7,8 @@ module IO = end module M = LexerUnit.Make (IO) (Lexer.Make (LexToken)) + +let () = + match M.trace () with + Stdlib.Ok () -> () + | Error msg -> Utils.highlight msg diff --git a/src/passes/1-parser/pascaligo/LexerMain.ml b/src/passes/1-parser/pascaligo/LexerMain.ml index 042b0930a..ba2925172 100644 --- a/src/passes/1-parser/pascaligo/LexerMain.ml +++ b/src/passes/1-parser/pascaligo/LexerMain.ml @@ -10,5 +10,5 @@ module M = LexerUnit.Make (IO) (Lexer.Make (LexToken)) let () = match M.trace () with - Stdlib.Ok _ -> () - | Error msg -> Printf.eprintf "\027[31m%s\027[0m%!" msg + Stdlib.Ok () -> () + | Error msg -> Utils.highlight msg diff --git a/src/passes/1-parser/reasonligo/LexerMain.ml b/src/passes/1-parser/reasonligo/LexerMain.ml index 756a2f103..8681f49e8 100644 --- a/src/passes/1-parser/reasonligo/LexerMain.ml +++ b/src/passes/1-parser/reasonligo/LexerMain.ml @@ -7,3 +7,8 @@ module IO = end module M = LexerUnit.Make (IO) (Lexer.Make (LexToken)) + +let () = + match M.trace () with + Stdlib.Ok () -> () + | Error msg -> Utils.highlight msg diff --git a/src/passes/1-parser/shared/Lexer.mli b/src/passes/1-parser/shared/Lexer.mli index 1d338d719..034f8b252 100644 --- a/src/passes/1-parser/shared/Lexer.mli +++ b/src/passes/1-parser/shared/Lexer.mli @@ -136,7 +136,7 @@ module type S = val slide : token -> window -> window type instance = { - read : ?log:logger -> Lexing.lexbuf -> token; + read : log:logger -> Lexing.lexbuf -> token; buffer : Lexing.lexbuf; get_win : unit -> window; get_pos : unit -> Pos.t; diff --git a/src/passes/1-parser/shared/Lexer.mll b/src/passes/1-parser/shared/Lexer.mll index 73b33b804..97d864d52 100644 --- a/src/passes/1-parser/shared/Lexer.mll +++ b/src/passes/1-parser/shared/Lexer.mll @@ -158,7 +158,7 @@ module type S = val slide : token -> window -> window type instance = { - read : ?log:logger -> Lexing.lexbuf -> token; + read : log:logger -> Lexing.lexbuf -> token; buffer : Lexing.lexbuf; get_win : unit -> window; get_pos : unit -> Pos.t; @@ -424,13 +424,14 @@ module Make (Token: TOKEN) : (S with module Token = Token) = Hint: Remove the leading minus sign.\n" | Broken_string -> "The string starting here is interrupted by a line break.\n\ - Hint: Remove the break, close the string before or insert a backslash.\n" + Hint: Remove the break, close the string before or insert a \ + backslash.\n" | Invalid_character_in_string -> "Invalid character in string.\n\ Hint: Remove or replace the character.\n" | Reserved_name s -> - "Reserved name: " ^ s ^ ".\n\ - Hint: Change the name.\n" + sprintf "Reserved name: \"%s\".\n\ + Hint: Change the name.\n" s | Invalid_symbol -> "Invalid symbol.\n\ Hint: Check the LIGO syntax you use.\n" @@ -779,7 +780,7 @@ and scan_line thread state = parse and thread = push_string nl thread and state = {state with pos = state.pos#new_line nl} in thread, state } -| eof { fail thread.opening Unterminated_comment } +| eof { thread, state } | _ { let () = rollback lexbuf in let len = thread.len in let thread, @@ -855,7 +856,7 @@ and scan_utf8 thread state = parse type logger = Markup.t list -> token -> unit type instance = { - read : ?log:logger -> Lexing.lexbuf -> token; + read : log:logger -> Lexing.lexbuf -> token; buffer : Lexing.lexbuf; get_win : unit -> window; get_pos : unit -> Pos.t; @@ -934,7 +935,7 @@ let open_token_stream file_path_opt = in fail region Missing_break | _ -> () in - let rec read_token ?(log=fun _ _ -> ()) buffer = + let rec read_token ~log buffer = match FQueue.deq !state.units with None -> scan buffer; diff --git a/src/passes/1-parser/shared/ParserAPI.ml b/src/passes/1-parser/shared/ParserAPI.ml index aabb1efef..2f0ed7598 100644 --- a/src/passes/1-parser/shared/ParserAPI.ml +++ b/src/passes/1-parser/shared/ParserAPI.ml @@ -2,6 +2,12 @@ module Region = Simple_utils.Region +module type IO = + sig + val ext : string (* LIGO file extension *) + val options : EvalOpt.options (* CLI options *) + end + module type PARSER = sig (* The type of tokens, abstract syntax trees and expressions *) @@ -42,7 +48,8 @@ module type PARSER = (* Main functor *) -module Make (Lexer: Lexer.S) +module Make (IO : IO) + (Lexer: Lexer.S) (Parser: PARSER with type token = Lexer.Token.token) (ParErr: sig val message : int -> string end) = struct @@ -116,18 +123,21 @@ module Make (Lexer: Lexer.S) module Incr = Parser.Incremental + module Log = LexerLog.Make (Lexer) + let log = Log.output_token ~offsets:IO.options#offsets + IO.options#mode IO.options#cmd stdout + let incr_contract Lexer.{read; buffer; get_win; close; _} = - let supplier = I.lexer_lexbuf_to_supplier read buffer - and failure = failure get_win in - let parser = Incr.contract buffer.Lexing.lex_curr_p in - let ast = I.loop_handle success failure supplier parser + let supplier = I.lexer_lexbuf_to_supplier (read ~log) buffer + and failure = failure get_win in + let parser = Incr.contract buffer.Lexing.lex_curr_p in + let ast = I.loop_handle success failure supplier parser in close (); ast - let incr_expr Lexer.{read; buffer; get_win; close; _} = - let supplier = I.lexer_lexbuf_to_supplier read buffer - and failure = failure get_win in - let parser = Incr.interactive_expr buffer.Lexing.lex_curr_p in - let expr = I.loop_handle success failure supplier parser + let incr_expr Lexer.{read; buffer; get_win; close; _} = + let supplier = I.lexer_lexbuf_to_supplier (read ~log) buffer + and failure = failure get_win in + let parser = Incr.interactive_expr buffer.Lexing.lex_curr_p in + let expr = I.loop_handle success failure supplier parser in close (); expr - end diff --git a/src/passes/1-parser/shared/ParserAPI.mli b/src/passes/1-parser/shared/ParserAPI.mli index 396a8698c..6fce46381 100644 --- a/src/passes/1-parser/shared/ParserAPI.mli +++ b/src/passes/1-parser/shared/ParserAPI.mli @@ -2,6 +2,12 @@ module Region = Simple_utils.Region +module type IO = + sig + val ext : string (* LIGO file extension *) + val options : EvalOpt.options (* CLI options *) + end + (* The signature generated by Menhir with additional type definitions for [ast] and [expr]. *) @@ -43,7 +49,8 @@ module type PARSER = end end -module Make (Lexer: Lexer.S) +module Make (IO: IO) + (Lexer: Lexer.S) (Parser: PARSER with type token = Lexer.Token.token) (ParErr: sig val message : int -> string end) : sig diff --git a/src/passes/1-parser/shared/ParserUnit.ml b/src/passes/1-parser/shared/ParserUnit.ml index dff827a56..eb4eb61d1 100644 --- a/src/passes/1-parser/shared/ParserUnit.ml +++ b/src/passes/1-parser/shared/ParserUnit.ml @@ -85,7 +85,7 @@ module Make (Lexer: Lexer.S) (* Instantiating the parser *) - module Front = ParserAPI.Make (Lexer)(Parser)(ParErr) + module Front = ParserAPI.Make (IO)(Lexer)(Parser)(ParErr) let format_error = Front.format_error From e25bb009619cfb8aa8885683b02716ad053e7b99 Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Fri, 31 Jan 2020 14:11:56 +0100 Subject: [PATCH 4/9] Forgot to add those fixes to the previous commit. --- src/passes/1-parser/cameligo/LexerMain.ml | 4 ++-- src/passes/1-parser/pascaligo/LexerMain.ml | 2 +- src/passes/1-parser/reasonligo/LexerMain.ml | 4 ++-- src/passes/1-parser/shared/Lexer.mll | 22 ++++++++++----------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/passes/1-parser/cameligo/LexerMain.ml b/src/passes/1-parser/cameligo/LexerMain.ml index 6c0729851..5ef471c37 100644 --- a/src/passes/1-parser/cameligo/LexerMain.ml +++ b/src/passes/1-parser/cameligo/LexerMain.ml @@ -1,4 +1,4 @@ -(** Driver for the CameLIGO lexer *) +(* Driver for the CameLIGO lexer *) module IO = struct @@ -11,4 +11,4 @@ module M = LexerUnit.Make (IO) (Lexer.Make (LexToken)) let () = match M.trace () with Stdlib.Ok () -> () - | Error msg -> Utils.highlight msg + | Error Region.{value; _} -> Utils.highlight value diff --git a/src/passes/1-parser/pascaligo/LexerMain.ml b/src/passes/1-parser/pascaligo/LexerMain.ml index f02b06642..32606118a 100644 --- a/src/passes/1-parser/pascaligo/LexerMain.ml +++ b/src/passes/1-parser/pascaligo/LexerMain.ml @@ -13,4 +13,4 @@ module M = LexerUnit.Make (IO) (Lexer.Make (LexToken)) let () = match M.trace () with Stdlib.Ok () -> () - | Error msg -> Utils.highlight msg + | Error Region.{value; _} -> Utils.highlight value diff --git a/src/passes/1-parser/reasonligo/LexerMain.ml b/src/passes/1-parser/reasonligo/LexerMain.ml index 8681f49e8..f0589990c 100644 --- a/src/passes/1-parser/reasonligo/LexerMain.ml +++ b/src/passes/1-parser/reasonligo/LexerMain.ml @@ -1,4 +1,4 @@ -(** Driver for the ReasonLIGO lexer *) +(* Driver for the ReasonLIGO lexer *) module IO = struct @@ -11,4 +11,4 @@ module M = LexerUnit.Make (IO) (Lexer.Make (LexToken)) let () = match M.trace () with Stdlib.Ok () -> () - | Error msg -> Utils.highlight msg + | Error Region.{value; _} -> Utils.highlight value diff --git a/src/passes/1-parser/shared/Lexer.mll b/src/passes/1-parser/shared/Lexer.mll index ca85a124e..2821acc85 100644 --- a/src/passes/1-parser/shared/Lexer.mll +++ b/src/passes/1-parser/shared/Lexer.mll @@ -823,17 +823,17 @@ and scan_utf8 thread state = parse context of a recognised lexeme (to enforce stylistic constraints or report special error patterns), we need to keep a hidden reference to a queue of recognised lexical units (that is, tokens and markup) - that acts as a mutable state between the calls to - [read_token]. When [read_token] is called, that queue is examined - first and, if it contains at least one token, that token is - returned; otherwise, the lexing buffer is scanned for at least one - more new token. That is the general principle: we put a high-level - buffer (our queue) on top of the low-level lexing buffer. + that acts as a mutable state between the calls to [read]. When + [read] is called, that queue is examined first and, if it contains + at least one token, that token is returned; otherwise, the lexing + buffer is scanned for at least one more new token. That is the + general principle: we put a high-level buffer (our queue) on top of + the low-level lexing buffer. One tricky and important detail is that we must make any parser - generated by Menhir (and calling [read_token]) believe that the - last region of the input source that was matched indeed corresponds - to the returned token, despite that many tokens and markup may have + generated by Menhir (and calling [read]) believe that the last + region of the input source that was matched indeed corresponds to + the returned token, despite that many tokens and markup may have been matched since it was actually read from the input. In other words, the parser requests a token that is taken from the high-level buffer, but the parser requests the source regions from @@ -858,7 +858,7 @@ and scan_utf8 thread state = parse distinguish the first call to the function [scan], as the first scanning rule is actually [init] (which can handle the BOM), not [scan]. -*) + *) type logger = Markup.t list -> token -> unit @@ -952,7 +952,7 @@ let open_token_stream input = in fail region Missing_break | _ -> () in - let rec read_token ~log buffer = + let rec read ~log buffer = match FQueue.deq !state.units with None -> scan buffer; From 410bbb0723a73adca2df3a3040098901d72d4062 Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Wed, 5 Feb 2020 16:28:40 +0100 Subject: [PATCH 5/9] Complete rewrite of the documentation. --- .../docs/advanced/entrypoints-contracts.md | 497 ++++++---- .../src/entrypoints-contracts/amount.ligo | 4 - .../src/entrypoints-contracts/owner.ligo | 5 - .../transaction/counter.ligo | 9 - .../transaction/counter.types.ligo | 4 - .../transaction/proxy.ligo | 14 - .../advanced/src/timestamps/timestamp.ligo | 9 - .../docs/advanced/timestamps-addresses.md | 141 +-- .../docs/language-basics/boolean-if-else.md | 202 ++-- .../docs/language-basics/functions.md | 171 +++- gitlab-pages/docs/language-basics/loops.md | 245 +++-- .../docs/language-basics/maps-records.md | 932 +++++++++++------- .../docs/language-basics/math-numbers-tez.md | 256 +++-- .../docs/language-basics/sets-lists-tuples.md | 738 ++++++++------ .../src/boolean-if-else/cond.ligo | 4 + .../src/boolean-if-else/cond.mligo | 4 + .../src/boolean-if-else/cond.religo | 4 + .../src/boolean-if-else/if-else.ligo | 4 - .../language-basics/src/functions/add.ligo | 4 - .../language-basics/src/functions/anon.ligo | 7 +- .../language-basics/src/functions/anon.mligo | 2 + .../language-basics/src/functions/anon.religo | 2 + .../src/functions/blockless.ligo | 2 +- .../src/functions/blockless.mligo | 1 + .../src/functions/blockless.religo | 1 + .../language-basics/src/functions/curry.mligo | 3 + .../language-basics/src/loops/collection.ligo | 22 + .../docs/language-basics/src/loops/gcd.ligo | 14 + .../docs/language-basics/src/loops/gcd.mligo | 7 + .../docs/language-basics/src/loops/gcd.religo | 7 + .../docs/language-basics/src/loops/sum.ligo | 6 + .../src/maps-records/record_patch.ligo | 10 + .../src/maps-records/record_patch2.ligo | 9 + .../src/maps-records/record_simu.ligo | 8 + .../src/maps-records/record_update.ligo | 7 + .../src/maps-records/record_update.mligo | 7 + .../src/maps-records/record_update.religo | 7 + .../src/math-numbers-tez/addition.ligo | 13 - .../src/math-numbers-tez/casting.ligo | 2 - .../src/math-numbers-tez/division.ligo | 5 - .../src/math-numbers-tez/isnat.ligo | 1 - .../src/math-numbers-tez/multiplication.ligo | 3 - .../src/math-numbers-tez/substraction.ligo | 6 - .../empty-set.ligo | 0 .../src/sets-lists-tuples/lists.ligo | 12 + .../src/sets-lists-tuples/sets.ligo | 28 + .../src/sets-lists-tuples/sets.mligo | 16 + .../src/sets-lists-tuples/sets.religo | 16 + .../language-basics/src/strings/concat.ligo | 1 - .../docs/language-basics/src/types/alias.ligo | 2 - .../language-basics/src/types/annotation.ligo | 4 - .../src/types/composed-types.ligo | 19 - .../src/types/simple-type.ligo | 11 +- .../unit-option-pattern-matching/flip.ligo | 7 + .../src/variables-and-constants/add.ligo | 11 +- .../src/variables-and-constants/add.mligo | 2 + .../src/variables-and-constants/add.religo | 4 +- .../src/variables-and-constants/const.mligo | 1 + gitlab-pages/docs/language-basics/strings.md | 61 +- .../docs/language-basics/tezos-specific.md | 133 +-- gitlab-pages/docs/language-basics/types.md | 196 ++-- .../unit-option-pattern-matching.md | 248 +++-- .../variables-and-constants.md | 111 ++- src/bin/expect_tests/lexer_tests.ml | 6 +- 64 files changed, 2633 insertions(+), 1655 deletions(-) delete mode 100644 gitlab-pages/docs/advanced/src/entrypoints-contracts/amount.ligo delete mode 100644 gitlab-pages/docs/advanced/src/entrypoints-contracts/owner.ligo delete mode 100644 gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/counter.ligo delete mode 100644 gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/counter.types.ligo delete mode 100644 gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/proxy.ligo delete mode 100644 gitlab-pages/docs/advanced/src/timestamps/timestamp.ligo create mode 100644 gitlab-pages/docs/language-basics/src/boolean-if-else/cond.ligo create mode 100644 gitlab-pages/docs/language-basics/src/boolean-if-else/cond.mligo create mode 100644 gitlab-pages/docs/language-basics/src/boolean-if-else/cond.religo delete mode 100644 gitlab-pages/docs/language-basics/src/boolean-if-else/if-else.ligo delete mode 100644 gitlab-pages/docs/language-basics/src/functions/add.ligo create mode 100644 gitlab-pages/docs/language-basics/src/functions/anon.mligo create mode 100644 gitlab-pages/docs/language-basics/src/functions/anon.religo create mode 100644 gitlab-pages/docs/language-basics/src/functions/blockless.mligo create mode 100644 gitlab-pages/docs/language-basics/src/functions/blockless.religo create mode 100644 gitlab-pages/docs/language-basics/src/functions/curry.mligo create mode 100644 gitlab-pages/docs/language-basics/src/loops/collection.ligo create mode 100644 gitlab-pages/docs/language-basics/src/loops/gcd.ligo create mode 100644 gitlab-pages/docs/language-basics/src/loops/gcd.mligo create mode 100644 gitlab-pages/docs/language-basics/src/loops/gcd.religo create mode 100644 gitlab-pages/docs/language-basics/src/loops/sum.ligo create mode 100644 gitlab-pages/docs/language-basics/src/maps-records/record_patch.ligo create mode 100644 gitlab-pages/docs/language-basics/src/maps-records/record_patch2.ligo create mode 100644 gitlab-pages/docs/language-basics/src/maps-records/record_simu.ligo create mode 100644 gitlab-pages/docs/language-basics/src/maps-records/record_update.ligo create mode 100644 gitlab-pages/docs/language-basics/src/maps-records/record_update.mligo create mode 100644 gitlab-pages/docs/language-basics/src/maps-records/record_update.religo delete mode 100644 gitlab-pages/docs/language-basics/src/math-numbers-tez/addition.ligo delete mode 100644 gitlab-pages/docs/language-basics/src/math-numbers-tez/casting.ligo delete mode 100644 gitlab-pages/docs/language-basics/src/math-numbers-tez/division.ligo delete mode 100644 gitlab-pages/docs/language-basics/src/math-numbers-tez/isnat.ligo delete mode 100644 gitlab-pages/docs/language-basics/src/math-numbers-tez/multiplication.ligo delete mode 100644 gitlab-pages/docs/language-basics/src/math-numbers-tez/substraction.ligo rename gitlab-pages/docs/language-basics/src/{sets-lists-touples => sets-lists-tuples}/empty-set.ligo (100%) create mode 100644 gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.ligo create mode 100644 gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo create mode 100644 gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo create mode 100644 gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo delete mode 100644 gitlab-pages/docs/language-basics/src/strings/concat.ligo delete mode 100644 gitlab-pages/docs/language-basics/src/types/alias.ligo delete mode 100644 gitlab-pages/docs/language-basics/src/types/annotation.ligo delete mode 100644 gitlab-pages/docs/language-basics/src/types/composed-types.ligo create mode 100644 gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo create mode 100644 gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo create mode 100644 gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo diff --git a/gitlab-pages/docs/advanced/entrypoints-contracts.md b/gitlab-pages/docs/advanced/entrypoints-contracts.md index dff0a535a..c105d08f6 100644 --- a/gitlab-pages/docs/advanced/entrypoints-contracts.md +++ b/gitlab-pages/docs/advanced/entrypoints-contracts.md @@ -1,173 +1,227 @@ --- id: entrypoints-contracts -title: Entrypoints, Contracts +title: Entrypoints to Contracts --- ## Entrypoints -Each LIGO smart contract is essentially a single main function, referring to the following types: +A LIGO contract is made of a series of constant and function +declarations. Only functions having a special type can be called when +the contract is activated: they are called *entrypoints*. An +entrypoint need to take two parameters, the *contract parameter* and +the *on-chain storage*, and return a pair made of a *list of +operations* and a (new) storage. + +When the contract is originated, the initial value of the storage is +provided. When and entrypoint is later called, only the parameter is +provided, but the type of an entrypoint contains both. + +The type of the contract parameter and the storage are up to the +contract designer, but the type for list operations is not. The return +type of an entrypoint is as follows, assuming that the type `storage` +has been defined elsewhere. (Note that you can use any type with any +name for the storage.) +```pascaligo skip +type storage is ... // Any name, any type +type return is list (operation) * storage +``` + + +```cameligo skip +type storage = ... // Any name, any type +type return = operation list * storage +``` + + +```reasonligo skip +type storage = ...; // Any name, any type +type return = (list (operation), storage); +``` + + +The contract storage can only be modified by activating an +entrypoint. It is important to understand what that means. What it +does *not* mean is that some global variable holding the storage is +modified by the entrypoint. Instead, what it *does* mean is that, +given the state of the storage *on-chain*, an entrypoint specifies how +to create another state for it, depending on a parameter. + +Here is an example where the storage is a single natural number that +is updated by the parameter. + + + + + ```pascaligo group=a -type parameter_t is unit -type storage_t is unit -type return_t is (list(operation) * storage_t) +type storage is nat +type return is list (operation) * storage + +function save (const parameter : nat; const store : storage) : return is + ((nil : list (operation)), parameter) ``` ```cameligo group=a -type parameter_t = unit -type storage_t = unit -type return_t = (operation list * storage_t) +type storage = nat + +let save (parameter, store: nat * storage) : return = + (([] : operation list), parameter) ``` ```reasonligo group=a -type parameter_t = unit; -type storage_t = unit; -type return_t = (list(operation) , storage_t); -``` - +type storage = nat; -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* - - - -```pascaligo group=a -function main(const parameter: parameter_t; const store: storage_t): return_t is - ((nil : list(operation)), store) -``` - - -```cameligo group=a -let main (parameter, store: parameter_t * storage_t) : return_t = - (([]: operation list), store) -``` - - -```reasonligo group=a -let main = ((parameter, store): (parameter_t, storage_t)) : return_t => { - (([]: list(operation)), store); +let main = ((parameter, store): (nat, storage)) : return => { + (([] : list (operation)), parameter); }; ``` -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. +In LIGO, the design pattern for entrypoints consists in actually +having exactly *one entrypoint*, like the `main` function in C. The +parameter of the contract is then a variant type, and, depending on +the constructors of that type, different functions in the contract are +called. In other terms, the unique entrypoint dispatches the control +flow depending on a *pattern matching* on the contract parameter. -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. +In the following example, the storage contains a counter (of type +`nat`) and a name (of type `string`). Depending on the parameter of +the contract, either the counter or the name is updated. - -```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 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 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)) - } -}; -``` - - - -## Built-in contract variables - -Each LIGO smart contract deployed on the Tezos blockchain, has access to certain built-in variables/constants that can be used to determine a range -of useful things. In this section you'll find how those built-ins can be utilized. - -### Accepting/declining money in a smart contract - -This example shows how `amount` and `failwith` can be used to decline a transaction that sends more tez than `0mutez`. - - ```pascaligo group=b -function main (const p : unit ; const s : unit) : (list(operation) * unit) is - block { - if amount > 0mutez then failwith("This contract does not accept tez") else skip - } with ((nil : list(operation)), unit); +type parameter is + Entrypoint_A of nat +| Entrypoint_B of string + +type storage is record [ + counter : nat; + name : string +] + +type return is list (operation) * storage + +function handle_A (const n : nat; const store : storage) : return is + ((nil : list (operation)), store with record [counter = n]) + +function handle_B (const s : string; const store : storage) : return is + ((nil : list (operation)), store with record [name = s]) + +function main (const param : parameter; const store : storage): return is + case param of + Entrypoint_A (n) -> handle_A (n, store) + | Entrypoint_B (s) -> handle_B (s, store) + end ``` ```cameligo group=b -let main (p, s: unit * unit) : operation list * unit = - if amount > 0mutez - then (failwith "This contract does not accept tez": operation list * unit) - else (([]: operation list), unit) +type parameter = + Entrypoint_A of nat +| Entrypoint_B of string + +type storage = { + counter : nat; + name : string +} + +type return = operation list * storage + +let handle_A (n, store : nat * storage) : return = + ([] : operation list), {store with counter = n} + +let handle_B (s, store : string * storage) : return = + ([] : operation list), {store with name = s} + +let main (param, store: parameter * storage) : return = + match param with + Entrypoint_A n -> handle_A (n, store) + | Entrypoint_B s -> handle_B (s, store) ``` ```reasonligo group=b -let main = ((p,s): (unit, unit)) : (list(operation), unit) => { - if (amount > 0mutez) { - (failwith("This contract does not accept tez"): (list(operation), unit)); +type parameter = +| Entrypoint_A (nat) +| Entrypoint_B (string); + +type storage = { + counter : nat, + name : string +}; + +type return = (list (operation), storage); + +let handle_A = ((n, store): (nat, storage)) : return => { + (([] : list (operation)), {...store, counter : n}); }; + +let handle_B = ((s, store): (string, storage)) : return => { + (([] : list (operation)), {...store, name : s}); }; + +let main = ((param, store): (parameter, storage)) : return => { + switch (param) { + | Entrypoint_A (n) => handle_A ((n, store)) + | Entrypoint_B (s) => handle_B ((s, store)) } - else { - (([]: list(operation)), ()); - }; +}; +``` + + + +## Tezos-specific Built-ins + +A LIGO smart contract can query part of the state of the Tezos +blockchain by means of built-in values. In this section you will find +how those built-ins can be utilized. + +### Accepting or Declining Tokens in a Smart Contract + +This example shows how `amount` and `failwith` can be used to decline +any transaction that sends more tez than `0mutez`, that is, no +incoming tokens are accepted. + + + +```pascaligo group=c +type parameter is unit +type storage is unit +type return is list (operation) * storage + +function deny (const param : parameter; const store : storage) : return is + if amount > 0mutez then + (failwith ("This contract does not accept tokens.") : return) + else ((nil : list (operation)), store) +``` + + +```cameligo group=c +type parameter = unit +type storage = unit +type return = operation list * storage + +let deny (param, store : parameter * storage) : return = + if amount > 0mutez then + (failwith "This contract does not accept tokens.": return) + else (([] : operation list), store) +``` + + +```reasonligo group=c +type parameter = unit; +type storage = unit; +type return = (list (operation), storage); + +let deny = ((param, store): (parameter, storage)) : return => { + if (amount > 0mutez) { + (failwith("This contract does not accept tokens."): return); } + else { (([] : list (operation)), store); }; }; ``` @@ -180,128 +234,165 @@ This example shows how `sender` or `source` can be used to deny access to an ent ```pascaligo group=c -const owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); -function main (const p : unit ; const s : unit) : (list(operation) * unit) is - block { - if source =/= owner then failwith("This address can't call the contract") else skip - } with ((nil : list(operation)), unit); +const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); + +function filter (const param : parameter; const store : storage) : return is + if source =/= owner then (failwith ("Access denied.") : return) + else ((nil : list(operation)), store) ``` ```cameligo group=c -let owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -let main (p,s: unit * unit) : operation list * unit = - if source <> owner - then (failwith "This address can't call the contract": operation list * unit) - else (([]: operation list), ()) +let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) + +let filter (param, store: parameter * storage) : return = + if source <> owner then (failwith "Access denied." : return) + else (([] : operation list), store) ``` ```reasonligo group=c -let owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); -let main = ((p,s): (unit, unit)) : (list(operation), unit) => { - if (source != owner) { - (failwith("This address can't call the contract"): (list(operation), unit)); - } - else { - (([]: list(operation)), ()); - }; +let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); + +let main = ((param, store): (parameter, storage)) : storage => { + if (source != owner) { (failwith ("Access denied.") : return); } + else { (([] : list (operation)), store); }; }; ``` -### Cross contract calls +### Inter-Contract Invocations -This example shows how a contract can invoke another contract by emiting a transaction operation at the end of an entrypoint. +It would be somewhat misleading to speak of "contract calls", as this +wording may wrongly suggest an analogy between contract "calls" and +function "calls". Indeed, the control flow returns to the site of a +function call, and composed function calls therefore are *stacked*, +that is, they follow a last in, first out ordering. This is not what +happens when a contract invokes another: the invocation is *queued*, +that is, follows a first in, first our ordering, and the dequeuing +only starts at the normal end of a contract (no failure). That is why +we speak of "contract invocations" instead of "calls". -> The same technique can be used to transfer tez to an implicit account (tz1, ...), all you have to do is use `unit` instead of a parameter for a smart contract. +The following example shows how a contract can invoke another by +emiting a transaction operation at the end of an entrypoint. -In our case, we have a `counter.ligo` contract that accepts a parameter of type `action`, and we have a `proxy.ligo` contract that accepts the same parameter type, and forwards the call to the deployed counter contract. +> The same technique can be used to transfer tokens to an implicit +> account (tz1, ...): all you have to do is use a unit value as the +> parameter of the smart contract. + +In our case, we have a `counter.ligo` contract that accepts a +parameter of type `action`, and we have a `proxy.ligo` contract that +accepts the same parameter type, and forwards the call to the deployed +counter contract. + ```pascaligo skip // counter.ligo -type action is -| Increment of int -| Decrement of int -| Reset of unit +type parameter is + Increment of nat +| Decrement of nat +| Reset +type storage is unit + +type return is list (operation) * storage ``` -```pascaligo skip +```pascaligo group=d // proxy.ligo -type action is -| Increment of int -| Decrement of int -| Reset of unit +type parameter is + Increment of nat +| Decrement of nat +| Reset -const dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address); +type storage is unit -function proxy(const param: action; const store: unit): (list(operation) * unit) - is block { - const counter: contract(action) = get_contract(dest); - // re-use the param passed to the proxy in the subsequent transaction - // e.g.: - // const mockParam: action = Increment(5); - const op: operation = transaction(param, 0mutez, counter); - const opList: list(operation) = list op; end; - } with (opList, store) +type return is list (operation) * storage + +const dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address) + +function proxy (const param : parameter; const store : storage): return is + block { + const counter : contract (parameter) = get_contract (dest); + (* Reuse the parameter in the subsequent + transaction or use another one, `mock_param`. *) + const mock_param : parameter = Increment (5n); + const op : operation = transaction (param, 0mutez, counter); + const ops : list (operation) = list [op] + } with (ops, store) ``` + -```cameligo +```cameligo skip // counter.mligo -type action = -| Increment of int -| Decrement of int -| Reset of unit + +type paramater = + Increment of nat +| Decrement of nat +| Reset // ... ``` -```cameligo +```cameligo group=d // proxy.mligo -type action = -| Increment of int -| Decrement of int -| Reset of unit +type parameter = + Increment of nat +| Decrement of nat +| Reset -let dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address) +type storage = unit -let proxy (param, storage: action * unit): operation list * unit = - let counter: action contract = Operation.get_contract dest in - let op: operation = Operation.transaction param 0mutez counter in - [op], storage +type return = operation list * storage + +let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address) + +let proxy (param, store : parameter * storage) : return = + let counter : parameter contract = Operation.get_contract dest in + (* Reuse the parameter in the subsequent + transaction or use another one, `mock_param`. *) + let mock_param : parameter = Increment (5n) in + let op : operation = Operation.transaction param 0mutez counter + in [op], store ``` -```reasonligo +```reasonligo skip // counter.religo -type action = - | Increment(int) - | Decrement(int) - | Reset(unit); +type parameter = +| Increment (nat) +| Decrement (nat) +| Reset // ... ``` -```reasonligo +```reasonligo group=d // proxy.religo -type action = - | Increment(int) - | Decrement(int) - | Reset(unit); +type parameter = +| Increment (nat) +| Decrement (nat) +| Reset; -let dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address); +type storage = unit; -let proxy = ((param, s): (action, unit)): (list(operation), unit) => - let counter: contract(action) = Operation.get_contract(dest); - let op: operation = Operation.transaction(param, 0mutez, counter); - ([op], s); +type return = (list (operation), storage); + +let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address); + +let proxy = ((param, store): (parameter, storage)) : return => + let counter : contract (parameter) = Operation.get_contract (dest); + (* Reuse the parameter in the subsequent + transaction or use another one, `mock_param`. *) + let mock_param : parameter = Increment (5n); + let op : operation = Operation.transaction (param, 0mutez, counter); + ([op], store); ``` diff --git a/gitlab-pages/docs/advanced/src/entrypoints-contracts/amount.ligo b/gitlab-pages/docs/advanced/src/entrypoints-contracts/amount.ligo deleted file mode 100644 index fce71cde2..000000000 --- a/gitlab-pages/docs/advanced/src/entrypoints-contracts/amount.ligo +++ /dev/null @@ -1,4 +0,0 @@ -function main (const p : unit ; const s : unit) : (list(operation) * unit) is - block { - if amount > 0mutez then failwith("This contract does not accept tez") else skip - } with ((nil : list(operation)), unit); \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/entrypoints-contracts/owner.ligo b/gitlab-pages/docs/advanced/src/entrypoints-contracts/owner.ligo deleted file mode 100644 index b35b7c9fc..000000000 --- a/gitlab-pages/docs/advanced/src/entrypoints-contracts/owner.ligo +++ /dev/null @@ -1,5 +0,0 @@ -const owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); -function main (const p : unit ; const s : unit) : (list(operation) * unit) is - block { - if source =/= owner then failwith("This address can't call the contract") else skip - } with ((nil : list(operation)), unit); \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/counter.ligo b/gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/counter.ligo deleted file mode 100644 index 0c5f0c6d7..000000000 --- a/gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/counter.ligo +++ /dev/null @@ -1,9 +0,0 @@ -#include "counter.types.ligo" - -function counter (const p : action ; const s : int): (list(operation) * int) is - block { skip } with ((nil : list(operation)), - case p of - | Increment(n) -> s + n - | Decrement(n) -> s - n - | Reset(n) -> 0 - end) \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/counter.types.ligo b/gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/counter.types.ligo deleted file mode 100644 index be37bbf30..000000000 --- a/gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/counter.types.ligo +++ /dev/null @@ -1,4 +0,0 @@ -type action is -| Increment of int -| Decrement of int -| Reset of unit \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/proxy.ligo b/gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/proxy.ligo deleted file mode 100644 index 47b7b1f64..000000000 --- a/gitlab-pages/docs/advanced/src/entrypoints-contracts/transaction/proxy.ligo +++ /dev/null @@ -1,14 +0,0 @@ -#include "counter.types.ligo" - -// Replace the following address with your deployed counter contract address -const address: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address); - -function proxy(const param: action; const store: unit): (list(operation) * unit) - is block { - const counter: contract(action) = get_contract(address); - // re-use the param passed to the proxy in the subsequent transaction - // e.g.: - // const mockParam: action = Increment(5); - const op: operation = transaction(param, 0mutez, counter); - const opList: list(operation) = list op; end; - } with (opList, store) \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/timestamps/timestamp.ligo b/gitlab-pages/docs/advanced/src/timestamps/timestamp.ligo deleted file mode 100644 index 5c46e43b6..000000000 --- a/gitlab-pages/docs/advanced/src/timestamps/timestamp.ligo +++ /dev/null @@ -1,9 +0,0 @@ -const today: timestamp = now; -const one_day: int = 86400; -const in_24_hrs: timestamp = today + one_day; - -const today: timestamp = now; -const one_day: int = 86400; -const a_24_hrs_ago: timestamp = today - one_day; - -const not_tommorow: bool = (now = in_24_hrs) \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/timestamps-addresses.md b/gitlab-pages/docs/advanced/timestamps-addresses.md index e77ba76ee..dd7289e81 100644 --- a/gitlab-pages/docs/advanced/timestamps-addresses.md +++ b/gitlab-pages/docs/advanced/timestamps-addresses.md @@ -3,179 +3,204 @@ id: timestamps-addresses title: Timestamps, Addresses --- -## Timestamps +## Timestamps -Timestamps in LIGO, or in Michelson in general are available in smart contracts, while bakers baking the block (including the transaction in a block) are responsible for providing the given current timestamp for the contract. +LIGO features timestamps, as Michelson does, while bakers baking the +block (including the transaction in a block) are responsible for +providing the given current timestamp for the contract. -### Current time - -You can obtain the current time using the built-in syntax specific expression, please be aware that it's up to the baker to set the current timestamp value. +### Current Time +You can obtain the current time using the built-in syntax specific +expression, please be aware that it is up to the baker to set the +current timestamp value. ```pascaligo group=a -const today: timestamp = now; +const today : timestamp = now ``` ```cameligo group=a -let today: timestamp = Current.time +let today : timestamp = Current.time ``` ```reasonligo group=a -let today: timestamp = Current.time; +let today : timestamp = Current.time; ``` -> When running code with ligo CLI, the option `--predecessor-timestamp` allows you to control what `now` returns. +> When running code with ligo CLI, the option +> `--predecessor-timestamp` allows you to control what `now` returns. -### Timestamp arithmetic +### Timestamp Arithmetic -In LIGO, timestamps can be added with `int`(s), this enables you to set e.g. time constraints for your smart contracts like this: +In LIGO, timestamps can be added to integers, allowing you to set time +constraints on your smart contracts. Consider the following scenarios. #### In 24 hours + ```pascaligo group=b -const today: timestamp = now; -const one_day: int = 86400; -const in_24_hrs: timestamp = today + one_day; -const some_date: timestamp = ("2000-01-01T10:10:10Z" : timestamp); -const one_day_later: timestamp = some_date + one_day; +const today : timestamp = now +const one_day : int = 86400 +const in_24_hrs : timestamp = today + one_day +const some_date : timestamp = ("2000-01-01T10:10:10Z" : timestamp) +const one_day_later : timestamp = some_date + one_day ``` ```cameligo group=b -let today: timestamp = Current.time -let one_day: int = 86400 -let in_24_hrs: timestamp = today + one_day -let some_date: timestamp = ("2000-01-01t10:10:10Z" : timestamp) -let one_day_later: timestamp = some_date + one_day +let today : timestamp = Current.time +let one_day : int = 86400 +let in_24_hrs : timestamp = today + one_day +let some_date : timestamp = ("2000-01-01t10:10:10Z" : timestamp) +let one_day_later : timestamp = some_date + one_day ``` ```reasonligo group=b -let today: timestamp = Current.time; -let one_day: int = 86400; -let in_24_hrs: timestamp = today + one_day; -let some_date: timestamp = ("2000-01-01t10:10:10Z" : timestamp); -let one_day_later: timestamp = some_date + one_day; +let today : timestamp = Current.time; +let one_day : int = 86400; +let in_24_hrs : timestamp = today + one_day; +let some_date : timestamp = ("2000-01-01t10:10:10Z" : timestamp); +let one_day_later : timestamp = some_date + one_day; ``` -#### 24 hours ago +#### 24 hours Ago + ```pascaligo group=c -const today: timestamp = now; -const one_day: int = 86400; -const in_24_hrs: timestamp = today - one_day; +const today : timestamp = now +const one_day : int = 86400 +const in_24_hrs : timestamp = today - one_day ``` ```cameligo group=c -let today: timestamp = Current.time -let one_day: int = 86400 -let in_24_hrs: timestamp = today - one_day +let today : timestamp = Current.time +let one_day : int = 86400 +let in_24_hrs : timestamp = today - one_day ``` ```reasonligo group=c -let today: timestamp = Current.time; -let one_day: int = 86400; -let in_24_hrs: timestamp = today - one_day; +let today : timestamp = Current.time; +let one_day : int = 86400; +let in_24_hrs : timestamp = today - one_day; ``` -### Comparing timestamps +### Comparing Timestamps -You can also compare timestamps using the same comparison operators as for numbers: +You can also compare timestamps using the same comparison operators as +for numbers. ```pascaligo group=c -const not_tommorow: bool = (now = in_24_hrs) +const not_tommorow : bool = (now = in_24_hrs) ``` ```cameligo group=c -let not_tomorrow: bool = (Current.time = in_24_hrs) +let not_tomorrow : bool = (Current.time = in_24_hrs) ``` ```reasonligo group=c -let not_tomorrow: bool = (Current.time == in_24_hrs); +let not_tomorrow : bool = (Current.time == in_24_hrs); ``` ## Addresses -`address` is a LIGO datatype used for Tezos addresses (tz1, tz2, tz3, KT1, ...). - -Here's how you can define an address: +The type `address` in LIGO is used to denote Tezos addresses (tz1, +tz2, tz3, KT1, ...). Currently, addresses are created by casting a +string to the type `address`. Beware of failures if the address is +invalid. Consider the following examples. ```pascaligo group=d -const my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); +const my_account : address = + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) ``` ```cameligo group=d -let my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) +let my_account : address = + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) ``` ```reasonligo group=d -let my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); +let my_account : address = + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); ``` ## Signatures -`signature` is a LIGO datatype used for Tezos signature (edsig, spsig). +The type `signature` in LIGO datatype is used for Tezos signature +(edsig, spsig). Signatures are created by casting a string. Beware of +failure if the signature is invalid. -Here's how you can define a signature: +Here is how you can define a signature: ```pascaligo group=e -const my_signature: signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7": signature); +const my_sig : signature = + ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" : + signature) ``` ```cameligo group=e -let my_signature: signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7": signature) +let my_sig : signature = + ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" : + signature) ``` ```reasonligo group=e -let my_signature: signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7": signature); +let my_sig : signature = +("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" : +signature); ``` -## keys +## Keys -`key` is a LIGO datatype used for Tezos public key. +The type `key` in LIGO is used for Tezos public keys. Do not confuse +them with map keys. Keys are made by casting strings. Beware of +failure if the key is invalid. -Here's how you can define a key: +Here is how you can define a key. ```pascaligo group=f -const my_key: key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav": key); +const my_key : key = +("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key) ``` ```cameligo group=f -let my_key: key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav": key) +let my_key : key = + ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key) ``` ```reasonligo group=f -let my_key: key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav": key); +let my_key : key = + ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key); ``` diff --git a/gitlab-pages/docs/language-basics/boolean-if-else.md b/gitlab-pages/docs/language-basics/boolean-if-else.md index 07cb40d04..b6d3b0da3 100644 --- a/gitlab-pages/docs/language-basics/boolean-if-else.md +++ b/gitlab-pages/docs/language-basics/boolean-if-else.md @@ -1,61 +1,61 @@ --- id: boolean-if-else -title: Boolean, If, Else +title: Booleans and Conditionals --- -## Boolean +## Booleans -The type of a Boolean is `bool` and the possible values are `True` and `False`. - -Here's how to define a boolean: +The type of a boolean value is `bool`. Here is how to define a boolean +value: ```pascaligo group=a -const a: bool = True; -const b: bool = False; +const a : bool = True // Notice the capital letter +const b : bool = False // Same. ``` ```cameligo group=a -let a: bool = true -let b: bool = false +let a : bool = true +let b : bool = false ``` ```reasonligo group=a -let a: bool = true; -let b: bool = false; +let a : bool = true; +let b : bool = false; ``` -## Comparing two values +## Comparing two Values -In LIGO, only values of the same type can be compared. We call these "comparable types." Comparable types include e.g. `int`, `nat`, `string`, `tez`, `timestamp`, `address`, ... +In LIGO, only values of the same type can be compared. Moreover, not +all values of the same type can be compared, only those with +*comparable types*, which is a concept lifted from +Michelson. Comparable types include, for instance, `int`, `nat`, +`string`, `tez`, `timestamp`, `address`, etc. -### Comparing strings +### Comparing Strings ```pascaligo group=b -const a: string = "Alice"; -const b: string = "Alice"; -// True -const c: bool = (a = b); +const a : string = "Alice" +const b : string = "Alice" +const c : bool = (a = b) // True ``` ```cameligo group=b -let a: string = "Alice" -let b: string = "Alice" -// true -let c: bool = (a = b) +let a : string = "Alice" +let b : string = "Alice" +let c : bool = (a = b) // true ``` ```reasonligo group=b -let a: string = "Alice"; -let b: string = "Alice"; -(* true *) -let c: bool = (a == b); +let a : string = "Alice"; +let b : string = "Alice"; +let c : bool = (a == b); // true ``` @@ -65,128 +65,120 @@ let c: bool = (a == b); ```pascaligo group=c -const a: int = 5; -const b: int = 4; -const c: bool = (a = b); -const d: bool = (a > b); -const e: bool = (a < b); -const f: bool = (a <= b); -const g: bool = (a >= b); -const h: bool = (a =/= b); +const a : int = 5 +const b : int = 4 +const c : bool = (a = b) +const d : bool = (a > b) +const e : bool = (a < b) +const f : bool = (a <= b) +const g : bool = (a >= b) +const h : bool = (a =/= b) ``` ```cameligo group=c -let a: int = 5 -let b: int = 4 -let c: bool = (a = b) -let d: bool = (a > b) -let e: bool = (a < b) -let f: bool = (a <= b) -let g: bool = (a >= b) -let h: bool = (a <> b) +let a : int = 5 +let b : int = 4 +let c : bool = (a = b) +let d : bool = (a > b) +let e : bool = (a < b) +let f : bool = (a <= b) +let g : bool = (a >= b) +let h : bool = (a <> b) ``` ```reasonligo group=c -let a: int = 5; -let b: int = 4; -let c: bool = (a == b); -let d: bool = (a > b); -let e: bool = (a < b); -let f: bool = (a <= b); -let g: bool = (a >= b); -let h: bool = (a != b); +let a : int = 5; +let b : int = 4; +let c : bool = (a == b); +let d : bool = (a > b); +let e : bool = (a < b); +let f : bool = (a <= b); +let g : bool = (a >= b); +let h : bool = (a != b); ``` ### Comparing tez -> 💡 Comparing `tez` values is especially useful when dealing with an `amount` sent in a transaction. +> 💡 Comparing `tez` values is especially useful when dealing with an +> amount sent in a transaction. ```pascaligo group=d -const a: tez = 5mutez; -const b: tez = 10mutez; -const c: bool = (a = b); +const a : tez = 5mutez +const b : tez = 10mutez +const c : bool = (a = b) // false ``` ```cameligo group=d -let a: tez = 5mutez -let b: tez = 10mutez -// false -let c: bool = (a = b) +let a : tez = 5mutez +let b : tez = 10mutez +let c : bool = (a = b) // false ``` ```reasonligo group=d -let a: tez = 5mutez; -let b: tez = 10mutez; -(* false *) -let c: bool = (a == b); +let a : tez = 5mutez; +let b : tez = 10mutez; +let c : bool = (a == b); // false ``` -## Conditionals, if staments, and more +## Conditionals -Conditional logic is an important part of every real world program. - -### If/else statements +Conditional logic enables to fork the control flow depending on the +state. ```pascaligo group=e -const min_age: nat = 16n; +type magnitude is Small | Large // See variant types. -function is_adult(const age: nat): bool is - if (age > min_age) then True else False +function compare (const n : nat) : magnitude is + if n < 10n then Small (Unit) else Large (Unit) // Unit is needed for now. ``` -> You can run the function above with -> ``` -> ligo run-function -s pascaligo src/if-else.ligo is_adult 21n -> ``` +You can run the `compare` function defined above using the LIGO compiler +like this: +```shell +ligo run-function +gitlab-pages/docs/language-basics/boolean-if-else/cond.ligo compare 21n' +# Outputs: Large (Unit) +``` ```cameligo group=e -let min_age: nat = 16n +type magnitude = Small | Large // See variant types. -(** - - This function is really obnoxious, but it showcases - how the if statement and it's syntax can be used. - - Normally, you'd use `with (age > min_age)` instead. - -*) -let is_adult (age: nat) : bool = - if (age > min_age) then true else false +let compare (n : nat) : magnitude = + if n < 10n then Small else Large ``` + +You can run the `compare` function defined above using the LIGO compiler +like this: +```shell +ligo run-function +gitlab-pages/docs/language-basics/boolean-if-else/cond.mligo compare 21n' +# Outputs: Large +``` + ```reasonligo group=e -let min_age: nat = 16n; +type magnitude = | Small | Large; // See variant types. -(** - - This function is really obnoxious, but it showcases - how the if statement and it's syntax can be used. - - Normally, you'd use `with (age > min_age)` instead. - -*) - -let is_adult = (age: nat): bool => - if (age > min_age) { - true; - } else { - false; - }; +let compare = (n : nat) : magnitude => + if (n < 10n) { Small; } else { Large; }; ``` -> You can run the function above with -> ``` -> ligo run-function -s reasonligo src/if-else.religo is_adult 21n -> ``` +You can run the `compare` function defined above using the LIGO compiler +like this: +```shell +ligo run-function +gitlab-pages/docs/language-basics/boolean-if-else/cond.religo compare 21n' +# Outputs: Large +``` - \ No newline at end of file + diff --git a/gitlab-pages/docs/language-basics/functions.md b/gitlab-pages/docs/language-basics/functions.md index b8a839817..bcae8b468 100644 --- a/gitlab-pages/docs/language-basics/functions.md +++ b/gitlab-pages/docs/language-basics/functions.md @@ -3,24 +3,40 @@ id: functions title: Functions --- -Writing code is fun as long as it doesn't get out of hand. To make sure our code doesn't turn into spaghetti we can group some logic into functions. +Writing code is fun as long as it does not get out of hand. To make +sure our code does not turn into spaghetti, we can structure some +logic into functions. -## Instruction blocks +## Blocks -With `block`(s) you can wrap *instructions* and *expressions* into an isolated scope. -Each `block` needs to include at least one `instruction`, or a *placeholder* instruction called `skip`. +In PascaLIGO, *blocks* enable the sequential composition of +*instructions* into an isolated scope. Each `block` needs to include +at least one instruction. If we need a placeholder *placeholder*, we +use the instruction called `skip`, that leaves the state +invariant. The rationale for `skip` instead of a truly empty block is +that it prevents you from writing an empty block by mistake. ```pascaligo skip -// shorthand syntax +// terse style block { a := a + 1 } -// verbose syntax +// verbose style begin a := a + 1 end ``` +Blocks are more versatile than simply containing instructions: +they can also include *declarations* of values, like so: +```pascaligo skip +// terse style +block { const a : int = 1; } +// verbose style +begin + const a : int = 1; +end +``` @@ -32,54 +48,71 @@ end 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`: +Here is how you define a basic function that computes the sum of two +integers: ```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 + block { + const sum : int = a + b + } with sum ``` The function body consists of two parts: -- `block {}` - logic of the function -- `with ` - the return value of the function +- `block { }` - logic of the function +- `with ` - the value returned by the function #### 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 expression +can be defined without a block. Instead of a block, you put an +expression, whose value is implicitly returned by the function, like +so: ```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 +``` + +You can call the function `add` defined above using the LIGO compiler +like this: +```shell +ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.ligo add '(1,2)' +# Outputs: 3 ``` -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 other +values. The difference is that a succession of parameters is provided +after the value name, followed by the return type. This follows OCaml +syntax. For example: +```cameligo group=c +let add (a : int) (b : int) : int = a + b +``` + +You can call the function `add` defined above using the LIGO compiler +like this: +```shell +ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.mligo add '(1,2)' +# Outputs: 3 +``` 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 +to in imperative 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 +useful because it means that CameLIGO supports [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 +it is costlier in Michelson than naive function execution accepting +multiple arguments. Instead, for most functions with more than one +parameter, we should gather the arguments in a [tuple](language-basics/sets-lists-tuples.md) and pass the tuple in as a single parameter. @@ -87,56 +120,88 @@ 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 +let add (a, b : int * int) : int = a + b // Uncurried +let add_curry (a : int) (b : int) : int = add (a, b) // Curried +let increment (b : int) : int = add_curry 1 // Partial application ``` -The function body is a series of expressions, which are evaluated to -give the return value. +You can run the `increment` function defined above using the LIGO +compiler like this: +```shell +ligo run-function gitlab-pages/docs/language-basics/src/functions/curry.mligo increment 5 +# Outputs: 6 +``` + +The function body is a single expression, whose value is returned. - 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 is how you define a basic function that accepts two `int`s and -returns an `int` as well: +other values. The difference is that a succession of parameters is +provided after the value name, followed by the return type. +Here is how you define a basic function that sums two integers: ```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 value. +You can call the function `add` defined above using the LIGO compiler +like this: +```shell +ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.religo add '(1,2)' +# Outputs: 3 +``` +The function body is a single expression, whose value is returned. -## Anonymous functions +## Anonymous functions (a.k.a. lambdas) -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. +It is possible to define functions without assigning them a name. They +are useful when you want to pass them as arguments, or assign them to +a key in a record or a map. + +Here is how to define an anonymous function: -Here's how to define an anonymous function assigned to a variable -`increment`, with it is appropriate function type signature. ```pascaligo group=c -const increment : int -> int = function (const i : int) : int is i + 1; -// a = 2 -const a: int = increment (1); +function increment (const b : int) : int is + (function (const a : int) : int is a + 1) (b) + +const a : int = increment (1); // a = 2 +``` + +You can check the value of `a` defined above using the LIGO compiler +like this: +```shell +ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.ligo a +# Outputs: 2 ``` ```cameligo group=c -let increment : int -> int = fun (i: int) -> i + 1 +let increment (b : int) : int = (fun (a : int) -> a + 1) b +let a : int = increment 1 // a = 2 +``` + +You can check the value of `a` defined above using the LIGO compiler +like this: +```shell +ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.mligo a +# Outputs: 2 ``` ```reasonligo group=c -let increment: (int => int) = (i: int) => i + 1; +let increment = (b : int) : int => ((a : int) : int => a + 1)(b); +let a : int = increment (1); // a = 2 +``` + +You can check the value of `a` defined above using the LIGO compiler +like this: +```shell +ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.religo a +# Outputs: 2 ``` diff --git a/gitlab-pages/docs/language-basics/loops.md b/gitlab-pages/docs/language-basics/loops.md index 9fd26d024..9a5a4d47b 100644 --- a/gitlab-pages/docs/language-basics/loops.md +++ b/gitlab-pages/docs/language-basics/loops.md @@ -3,108 +3,229 @@ id: loops title: Loops --- - - -## While Loop +## General Iteration -The PascaLIGO while loop should look familiar to users of imperative languages. -While loops are of the form `while `, and evaluate -their associated block until the condition evaluates to false. +General iteration in PascaLIGO takes the shape of "while" loops, which +should be familiar to programmers of imperative languages. Those loops +are of the form `while `. Their associated block is +repeatedly evaluated until the condition becomes true, or never +evaluated if the condition is false at the start. The loop never +terminates if the condition never becomes true. Because we are writing +smart contracts on Tezos, when the condition of a "while" loops fails +to become true, the execution will run out of gas and stop with a +failure anyway. -> ⚠️ The current PascaLIGO while loop has semantics that have diverged from other LIGO syntaxes. The goal of LIGO is that the various syntaxes express the same semantics, so this will be corrected in future versions. For details on how loops will likely work after refactoring, see the CameLIGO tab of this example. +Here is how to compute the greatest common divisors of two natural +number by means of Euclid's algorithm: -```pascaligo -function while_sum (var n : nat) : nat is block { - var i : nat := 0n ; - var r : nat := 0n ; - while i < n block { - i := i + 1n; - r := r + i; +```pascaligo group=a +function gcd (var x : nat; var y : nat) : nat is block { + if x < y then + block { + const z : nat = x; + x := y; y := z + } + else skip; + var r : nat := 0n; + while y =/= 0n block { + r := x mod y; + x := y; + y := r } -} with r +} with x +``` + +You can call the function `gcd` defined above using the LIGO compiler +like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/loops/gcd.ligo gcd '(2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)' +# Outputs: +12 ``` -`Loop.fold_while` is a fold operation that takes an initial value of a certain type -and then iterates on it until a condition is reached. The auxillary function -that does the fold returns either boolean true or boolean false to indicate -whether the fold should continue or not. The initial value must match the input -parameter of the auxillary function, and the auxillary should return type `(bool * input)`. +CameLIGO is a functional language where user-defined values are +constant, therefore it makes no sense in CameLIGO to feature loops, +which we understand as syntactic constructs where the state of a +stopping condition is mutated, as with "while" loops in PascaLIGO. -`continue` and `stop` are provided as syntactic sugar for the return values. +Instead, CameLIGO features a *fold operation* as a predefined function +named `Loop.fold_while`. It takes an initial value of a certain type, +called an *accumulator*, and repeatedly calls a given function, called +*iterated function*, that takes that accumulator and returns the next +value of the accumulator, until a condition is met and the fold stops +with the final value of the accumulator. The iterated function needs +to have a special type: if the type of the accumulator is `t`, then it +must have the type `bool * t` (not simply `t`). It is the boolean +value that denotes whether the stopping condition has been reached. -```cameligo -let aux (i: int) : bool * int = - if i < 100 then continue (i + 1) else stop i +```cameligo group=b +let iter (x,y : nat * nat) : bool * (nat * nat) = + if y = 0n then false, (x,y) else true, (y, x mod y) -let counter_simple (n: int) : int = - Loop.fold_while aux n +let gcd (x,y : nat * nat) : nat = + let x,y = if x < y then y,x else x,y in + let x,y = Loop.fold_while iter (x,y) + in x +``` + +To ease the writing and reading of the iterated functions (here, +`iter`), two predefined functions are provided: `continue` and `stop`: + +```cameligo group=c +let iter (x,y : nat * nat) : bool * (nat * nat) = + if y = 0n then stop (x,y) else continue (y, x mod y) + +let gcd (x,y : nat * nat) : nat = + let x,y = if x < y then y,x else x,y in + let x,y = Loop.fold_while iter (x,y) + in x +``` + +You can call the function `gcd` defined above using the LIGO compiler +like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/loops/gcd.mligo gcd (2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)' +# Outputs: +12 ``` -`Loop.fold_while` is a fold operation that takes an initial value of a certain type -and then iterates on it until a condition is reached. The auxillary function -that does the fold returns either boolean true or boolean false to indicate -whether the fold should continue or not. The initial value must match the input -parameter of the auxillary function, and the auxillary should return type `(bool, input)`. +ReasonLIGO is a functional language where user-defined values are +constant, therefore it makes no sense in ReasonLIGO to feature loops, +which we understand as syntactic constructs where the state of a +stopping condition is mutated, as with "while" loops in PascaLIGO. -`continue` and `stop` are provided as syntactic sugar for the return values. +Instead, ReasonLIGO features a *fold operation* as a predefined +function named `Loop.fold_while`. It takes an initial value of a +certain type, called an *accumulator*, and repeatedly calls a given +function, called *iterated function*, that takes that accumulator and +returns the next value of the accumulator, until a condition is met +and the fold stops with the final value of the accumulator. The +iterated function needs to have a special type: if the type of the +accumulator is `t`, then it must have the type `bool * t` (not simply +`t`). It is the boolean value that denotes whether the stopping +condition has been reached. -```reasonligo -let aux = (i: int): (bool, int) => - if (i < 100) { - continue(i + 1); - } else { - stop(i); - }; +```reasonligo group=d +let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) => + if (y == 0n) { (false, (x,y)); } else { (true, (y, x mod y)); }; -let counter_simple = (n: int): int => Loop.fold_while(aux, n); +let gcd = ((x,y) : (nat, nat)) : nat => + let (x,y) = if (x < y) { (y,x); } else { (x,y); }; + let (x,y) = Loop.fold_while (iter, (x,y)); + x; ``` +To ease the writing and reading of the iterated functions (here, +`iter`), two predefined functions are provided: `continue` and `stop`: + +```reasonligo group=e +let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) => + if (y == 0n) { stop ((x,y)); } else { continue ((y, x mod y)); }; + +let gcd = ((x,y) : (nat, nat)) : nat => + let (x,y) = if (x < y) { (y,x); } else { (x,y); }; + let (x,y) = Loop.fold_while (iter, (x,y)); + x; +``` -## For Loop +## For Loops -To iterate over a range of integers you use a loop of the form `for to `. +To iterate over a range of integers you use a loop of the form `for + to `, which is +familiar for programmers of imperative languages. Note that, for the +sake of generality, the bounds are of type `int`, not `nat`. -```pascaligo -function for_sum (var n : nat) : int is block { - var acc : int := 0 ; - for i := 1 to int(n) - begin - acc := acc + i; - end +```pascaligo group=f +function sum (var n : nat) : int is block { + var acc : int := 0; + for i := 1 to int (n) block { + acc := acc + i + } } with acc ``` +You can call the function `sum` defined above using the LIGO compiler +like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/loops/sum.ligo sum 7n +# Outputs: 28 +``` + -PascaLIGO for loops can also iterate through the contents of a collection. This is -done with a loop of the form `for in `. +PascaLIGO "for" loops can also iterate through the contents of a +collection, that is, a list, a set or a map. This is done with a loop +of the form `for in +`, where ` value` (or any other +variables). Give a map from strings to integers, here is how to sum +all the integers and concatenate all the strings. + +You can call the function `sum_map` defined above using the LIGO compiler +like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_map +'map ["1"->1; "2"->2; "3"->3]' +# Outputs: ( "123", 6 ) +``` diff --git a/gitlab-pages/docs/language-basics/maps-records.md b/gitlab-pages/docs/language-basics/maps-records.md index 3d05d41da..6a6572c37 100644 --- a/gitlab-pages/docs/language-basics/maps-records.md +++ b/gitlab-pages/docs/language-basics/maps-records.md @@ -1,419 +1,761 @@ --- id: maps-records -title: Maps, Records +title: Records and Maps --- So far we have seen pretty basic data types. LIGO also offers more -complex built-in constructs, such as maps and records. +complex built-in constructs, such as *records* and *maps*. + +## Records + +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 (`.`). + +Let us first consider and example of record type declaration. + + + +```pascaligo group=a +type user is + record [ + id : nat; + is_admin : bool; + name : string + ] +``` + + +```cameligo group=a +type user = { + id : nat; + is_admin : bool; + name : string +} +``` + + +```reasonligo group=a +type user = { + id : nat, + is_admin : bool, + name : string +}; +``` + + +And here is how a record value is defined: + + + +```pascaligo group=a +const alice : user = + record [ + id = 1n; + is_admin = True; + name = "Alice" + ] +``` + + +```cameligo group=a +let alice : user = { + id = 1n; + is_admin = true; + name = "Alice" +} +``` + + +```reasonligo group=a +let alice : user = { + id : 1n, + is_admin : true, + name : "Alice" +}; +``` + + +### Accessing Record Fields + +If we want the contents of a given field, we use the `.` infix +operator, like so: + + + +```pascaligo group=a +const alice_admin : bool = alice.is_admin +``` + + +```cameligo group=a +let alice_admin : bool = alice.is_admin +``` + + +```reasonligo group=a +let alice_admin: bool = alice.is_admin; +``` + + +### 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. + +One way to understand the update of record values is the *functional +update*. The idea is to have an *expression* whose value is the +updated record. The shape of that expression is ` +with `. The record variable is the record to update and +the record value is the update itself. + +Let us consider defining a function that translates three-dimensional +points on a plane. + + + + +```pascaligo group=b +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 + p with record [x = p.x + vec.dx; y = p.y + vec.dy] +``` + +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} +``` + +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. + + + +The syntax for the functional updates of record in CameLIGO follows +that of OCaml: + +```cameligo group=b +type point = {x : int; y : int; z : int} +type vector = {dx : int; dy : int} + +let origin : point = {x = 0; y = 0; z = 0} + +let xy_translate (p, vec : point * vector) : point = + {p with x = p.x + vec.dx; y = p.y + vec.dy} +``` + + +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} +``` + +> 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. + + + +The syntax for the functional updates of record in ReasonLIGO follows +that of OCaml: + +```reasonligo group=b +type point = {x : int, y : int, z : int}; +type vector = {dx : int, dy : int}; + +let origin : point = {x : 0, y : 0, z : 0}; + +let xy_translate = ((p, vec) : (point, vector)) : point => + {...p, x : p.x + vec.dx, y : p.y + vec.dy}; +``` + + +You can call the function `x_translation` defined above by running the +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} +``` + +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. + +### Imperative Updates + +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"). + +Let us consider defining a function that translates three-dimensional +points on a plane. + + + + +```pascaligo group=c +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 { + patch p with record [x = p.x + vec.dx]; + patch p with record [y = p.y + vec.dy] + } with p +``` + +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} +``` + +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=d +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 { + patch p with record [x = p.x + vec.dx; y = p.y + vec.dy] + } with p +``` + +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} +``` + +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=e +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 { + const p : point = p with record [x = p.x + vec.dx; y = p.y + vec.dy] +} with p +``` + +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} +``` + +The hiding of a variable by another (here `p`) is called `shadowing`. + + + ## 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 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. -Here is how a custom map type is defined: +Here is how a custom map from addresses to a pair of integers is +defined. -```pascaligo +```pascaligo group=f type move is int * int -type moveset is map(address, move) +type register is map (address, move) ``` - -```cameligo + +```cameligo group=f type move = int * int -type moveset = (address, move) map +type register = (address, move) map ``` - -```reasonligo + +```reasonligo group=f type move = (int, int); -type moveset = map(address, move); +type register = map (address, move); ``` - -And here is how a map value is populated: +And here is how a map value is defined: -```pascaligo -const moves: moveset = map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1, 2); - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0, 3); -end +```pascaligo group=f +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. -> -> `("": address)` means that we type-cast a string into an address. - +> Notice the `->` between the key and its value and `;` to separate +> individual map entries. The annotated value `("" : +> address)` means that we cast a string into an address. Also, `map` +> is a keyword. -```cameligo -let moves: moveset = Map.literal - [ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1, 2)) ; - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0, 3)) ; - ] + +```cameligo group=f +let moves : register = + Map.literal [ + (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2)); + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))] ``` -> 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. - +> The `Map.literal` predefined function builds a 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 = - Map.literal([ - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1, 2)), - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0, 3)), - ]); + +```reasonligo group=f +let moves : register = + Map.literal ([ + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)), + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3)),]); ``` -> Map.literal constructs the map from a list of key-value pair tuples, `(, )`. -> -> `("": address)` means that we type-cast a string into an address. + +> The `Map.literal` predefined function builds a 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. -### Accessing map values by key +### Accessing Map Bindings -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: +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 -const my_balance : option(move) = moves[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]; +```pascaligo group=f +(*const my_balance : option (move) = + moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] *) ``` - - -```cameligo -let my_balance : move option = Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves + +```cameligo group=f +let my_balance : move option = + Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves ``` - - -```reasonligo -let my_balance : option(move) = - Map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); + +```reasonligo group=f +let my_balance : option (move) = + 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: +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*. + -```pascaligo -const my_balance : move = get_force(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves); +```pascaligo group=f +function force_access (const key : address; const moves : register) : move is + case moves[key] of + Some (move) -> move + | None -> (failwith ("No move.") : move) + end ``` - - -```cameligo -let my_balance : move = Map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves + +```cameligo group=f +let force_access (key, moves : address * register) : move = + match Map.find_opt key moves with + Some move -> move + | None -> (failwith "No move." : move) ``` - - -```reasonligo -let my_balance : move = - Map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); + +```reasonlig group=f +let force_access : ((key, moves) : address * register) : move => { + switch (Map.find_opt key moves) with + Some move -> move + | None -> (failwith "No move." : move) +}; ``` - -### Updating the contents of a map + +### 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. We +may even want to retain the key but not the associated value. All +those operations are called *updates*. -The values of a PascaLIGO map can be updated using the ordinary assignment syntax: +The values of a PascaLIGO map can be updated using the usual +assignment syntax `[] := `. Let us +consider an example. -```pascaligo - -function set_ (var m: moveset) : moveset is +```pascaligo group=f +function assign (var m : register) : register is block { - m[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9); + m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9) + } with m +``` + +If multiple bindings need to be updated, PascaLIGO offers a *patch +instruction* for maps, similar to that for records. + +```pascaligo group=f +function assignments (var m : register) : register is + block { + patch m with map [ + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (4,9); + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2) + ] } with m ``` -We can update a map in CameLIGO using the `Map.update` built-in: +We can update a binding in a map in CameLIGO by means of the +`Map.update` built-in function: -```cameligo - -let updated_map: moveset = Map.update ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) (Some (4,9)) moves +```cameligo group=f +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 only +> defined on its key, but not its value. This encoding enables +> partially defined bindings. + -We can update a map in ReasonLIGO using the `Map.update` built-in: +We can update a binding in a map in ReasonLIGO by means of the +`Map.update` built-in function: -```reasonligo +```reasonligo group=f +let assign = (m : register) : register => { + Map.update + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), Some ((4,9)), m) +}; +``` -let updated_map: moveset = Map.update(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); +> 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 only +> defined on its key, but not its value. This encoding enables +> partially defined bindings. + + + +To remove a binding from a map, we need its key. + + + + + +In PascaLIGO, there is a special instruction to remove a binding from +a map. +```pascaligo group=f +function delete (const key : address; var moves : register) : register is + block { + remove key from map moves + } with moves +``` + + +```cameligo group=f +let delete (key, moves : address * register) : register = + Map.remove key moves +``` + + +```reasonligo group=f +let delete = ((key, moves) : (address, register)) : register => { + Map.remove (key, moves); +}; ``` -### Iteration over the contents of a map +### Iterating Functionally over a Map -There are three kinds of iteration on LIGO maps, `iter`, `map` and `fold`. `iter` -is an iteration over the map with no return value, its only use is to -generate 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, with an error thrown -otherwise. +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). + +There are three kinds of functional iteration over LIGO maps: `iter`, +`map` and `fold`. The first, `iter`, is an iteration over the map with +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. -```pascaligo -function iter_op (const m : moveset) : unit is +```pascaligo group=f +function iter_op (const m : register) : 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); + function aggregate (const i : address; const j : move) : unit is block + { if j.1 > 1 then skip else failwith ("Below range.") } with unit + } with map_iter (aggregate, m) ``` - -```cameligo -let iter_op (m : moveset) : unit = - let assert_eq = fun (i,j: address * move) -> assert (j.0 > 1) + +```cameligo group=f +let iter_op (m : register) : unit = + let assert_eq = fun (i,j : address * move) -> assert (j.0 > 1) in Map.iter assert_eq m ``` - -```reasonligo -let iter_op = (m: moveset): unit => { - let assert_eq = ((i,j): (address, move)) => assert (j[0] > 1); - Map.iter(assert_eq, m); + +```reasonligo group=f +let iter_op = (m : register) : unit => { + let assert_eq = ((i,j) : (address, move)) => assert (j[0] > 1); + Map.iter (assert_eq, m); }; ``` -`map` is a way to create a new map by modifying the contents of an existing one. +We may want to change all the bindings of a map by applying to them a +function. This is also called a *map operation*, as opposed to the +*map data structure* we have been presenting. -```pascaligo -function map_op (const m : moveset) : moveset is +```pascaligo group=f +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); + function increment (const i : address; const j : move) : move is + (j.0, j.1 + 1); + } with map_map (increment, m) ``` - -```cameligo -let map_op (m : moveset) : moveset = - let increment = fun (i,j: address * move) -> (j.0, j.1 + 1) + +```cameligo group=f +let map_op (m : register) : register = + let increment = fun (i,j : address * move) -> j.0, j.1 + 1 in Map.map increment m ``` - -```reasonligo -let map_op = (m: moveset): moveset => { + +```reasonligo group=f +let map_op = (m : register) : register => { let increment = ((i,j): (address, move)) => (j[0], j[1] + 1); Map.map(increment, m); }; ``` -`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. -It eventually returns the result of combining all the elements. +A *fold operation* is the most general of iterations. The iterated +function takes two arguments: an *accumulator* and the structure +*element* at hand, with which it then produces a new accumulator. This +enables to have a partial result that becomes complete when the +traversal of the data structure is over. -```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) +```pascaligo group=f +function fold_op (const m : register) : int is block { + function aggregate (const j : int; const cur : address * move) : int is + j + cur.1.1 + } with map_fold (aggregate, m, 5) ``` - -```cameligo -let fold_op (m : moveset) : moveset = - let aggregate = fun (i,j: int * (address * (int * int))) -> i + j.1.1 + +```cameligo group=f +let fold_op (m : register) : register = + let aggregate = fun (i,j : int * (address * move)) -> i + j.1.1 in Map.fold aggregate m 5 ``` - -```reasonligo -let fold_op = (m: moveset): moveset => { - let aggregate = ((i,j): (int, (address, (int,int)))) => i + j[1][1]; - Map.fold(aggregate, m, 5); + +```reasonligo group=f +let fold_op = (m: register): register => { + let aggregate = ((i,j): (int, (address, move))) => i + j[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. +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. Here is how we define a big map: -```pascaligo -type move is (int * int) -type moveset is big_map (address, move) +```pascaligo group=g +type move is int * int + +type register is big_map (address, move) ``` - -```cameligo + +```cameligo group=g type move = int * int -type moveset = (address, move) big_map + +type register = (address, move) big_map ``` - -```reasonligo + +```reasonligo group=g type move = (int, int); -type moveset = big_map(address, move); -``` +type register = big_map(address, move); +``` -And here is how a map value is populated: +And here is how a map value is created: -```pascaligo -const moves: moveset = - big_map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1,2); - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0,3); - end +```pascaligo group=g +const moves : register = + big_map [ + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2); + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (0,3)] ``` -> 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. - +> Notice the right arrow `->` between the key and its value and the +> semicolon separating individual map entries. The value annotation +> `("" : address)` means that we cast a string into an +> address. -```cameligo -let moves: moveset = + + +```cameligo group=g +let moves : register = Big_map.literal [ - (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1,2)); - (("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, `(, )`. -> Note also the `;` to separate individual map entries. -> -> `("": address)` means that we cast a string into an address. - +> The predefind function `Big_map.literal` constructs a big map from a +> list of key-value pairs `(, )`. Note also the semicolon +> separating individual map entries. The annotated value `(" value>" : address)` means that we cast a string into an address. -```reasonligo -let moves: moveset = + + +```reasonligo group=g +let moves : register = Big_map.literal ([ - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1,2)), - ("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, `(, )`. -> -> `("": address)` means that we cast a string into an address. + +> The predefind function `Big_map.literal` constructs a big map from a +> list of key-value pairs `(, )`. Note also the semicolon +> separating individual map entries. The annotated value `(" value>" : address)` means that we cast a string into an address. + -### Accessing map values by key +### Accessing 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 will get will be wrapped as an optional; in our case -`option(move)`. Here is an example: +If we want to access a move from our `register` above, we can use the +postfix `[]` operator to read the associated `move` value. However, +the value we read is an optional value: in our case, of type `option +(move)`. Here is an example: -```pascaligo -const my_balance : option(move) = - moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] +```pascaligo group=g +const my_balance : option (move) = + moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] ``` - + -```cameligo +```cameligo group=g let my_balance : move option = - Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves + Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves ``` - + -```reasonligo -let my_balance : option(move) = - Big_map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); +```reasonligo group=g +let my_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 my_balance : move = - get_force (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves); -``` - - - -```cameligo -let my_balance : move = - Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves -``` - - - -```reasonligo -let my_balance : move = - Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); -``` - - - -### Updating the contents of a big map +### Updating a Big Map -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 +assignment syntax for ordinary maps -```pascaligo - -function set_ (var m : moveset) : moveset is +```pascaligo group=g +function assign (var m : register) : register is block { - m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9); + m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9) } with m ``` @@ -422,10 +764,10 @@ function set_ (var m : moveset) : moveset is 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 +```cameligo group=g +let updated_map : register = + Big_map.update + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) moves ``` @@ -433,102 +775,12 @@ let updated_map : moveset = 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); +```reasonligo group=g +let updated_map : register = + Big_map.update + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); ``` +### Removing Bindings from a Map + - -## Records - -Records are a construct introduced in LIGO, and are not natively -available in Michelson. The LIGO compiler translates records into -Michelson `Pairs`. - -Here is how a custom record type is defined: - - - -```pascaligo -type user is - record - id : nat; - is_admin : bool; - name : string - end -``` - - -```cameligo -type user = { - id : nat; - is_admin : bool; - name : string -} -``` - - -```reasonligo -type user = { - id : nat, - is_admin : bool, - name : string -}; -``` - - - -And here is how a record value is populated: - - - -```pascaligo -const user : user = - record - id = 1n; - is_admin = True; - name = "Alice" - end -``` - - -```cameligo -let user : user = { - id = 1n; - is_admin = true; - name = "Alice" -} -``` - - -```reasonligo -let user : user = { - id : 1n, - is_admin : true, - name : "Alice" -}; -``` - - -### Accessing record keys by name - -If we want to obtain a value from a record for a given key, we can do -the following: - - - -```pascaligo -const is_admin : bool = user.is_admin; -``` - - -```cameligo -let is_admin : bool = user.is_admin -``` - - -```reasonligo -let is_admin: bool = user.is_admin; -``` - diff --git a/gitlab-pages/docs/language-basics/math-numbers-tez.md b/gitlab-pages/docs/language-basics/math-numbers-tez.md index 91a4b923a..859c73097 100644 --- a/gitlab-pages/docs/language-basics/math-numbers-tez.md +++ b/gitlab-pages/docs/language-basics/math-numbers-tez.md @@ -7,120 +7,156 @@ LIGO offers three built-in numerical types: `int`, `nat` and `tez`. ## Addition -Addition in ligo is acomplished by using the `+` operator. Some type constraints apply; for example you can't add `tez + nat`. +Addition in LIGO is accomplished by means of the `+` infix +operator. Some type constraints apply, for example you ca not add a +value of type `tez` to a value of type `nat`. -In the following example you can find a series of arithmetic operations, including various numerical types. However, some bits of the example won't compile because adding an `int` to a `nat` produces an `int`, not a `nat`. Similiar rules apply for `tez`: +In the following example you can find a series of arithmetic +operations, including various numerical types. However, some bits +remain in comments because they would otherwise not compile because, +for example, adding a value of type `int` to a value of type `tez` is +invalid. Note that adding an integer to a natural number produces an +integer. ```pascaligo group=a -// int + int produces int -const a: int = 5 + 10; -// nat + int produces int -const b: int = 5n + 10; -// tez + tez produces tez -const c: tez = 5mutez + 10mutez; -// you can't add tez + int or tez + nat, this won't compile -// const d: tez = 5mutez + 10n; -// two nats produce a nat -const e: nat = 5n + 10n; -// nat + int produces an int, this won't compile -// const f: nat = 5n + 10; -const g: int = 1_000_000; +// int + int yields int +const a : int = 5 + 10 + +// nat + int yields int +const b : int = 5n + 10 + +// tez + tez yields tez +const c : tez = 5mutez + 10mutez + +//tez + int or tez + nat is invalid +// const d : tez = 5mutez + 10n + +// two nats yield a nat +const e : nat = 5n + 10n + +// nat + int yields an int: invalid +// const f : nat = 5n + 10; + +const g : int = 1_000_000 ``` -> Pro tip: you can use underscores for readability when defining large numbers +> Pro tip: you can use underscores for readability when defining large +> numbers: > >```pascaligo ->const g: int = 1_000_000; +> const sum : tez = 100_000mutez >``` ```cameligo group=a -// int + int produces int -let a: int = 5 + 10 -// nat + int produces int -let b: int = 5n + 10 -// tez + tez produces tez -let c: tez = 5mutez + 10mutez -// you can't add tez + int or tez + nat, this won't compile -// const d: tez = 5mutez + 10n -// two nats produce a nat -let e: nat = 5n + 10n -// nat + int produces an int, this won't compile -// const f: nat = 5n + 10 -let g: int = 1_000_000 +// int + int yields int +let a : int = 5 + 10 + +// nat + int yields int +let b : int = 5n + 10 + +// tez + tez yields tez +let c : tez = 5mutez + 10mutez + +// tez + int or tez + nat is invalid +// const d : tez = 5mutez + 10n + +// two nats yield a nat +let e : nat = 5n + 10n + +// nat + int yields an int: invalid +// const f : nat = 5n + 10 + +let g : int = 1_000_000 ``` -> Pro tip: you can use underscores for readability when defining large numbers +> Pro tip: you can use underscores for readability when defining large +> numbers: > >```cameligo ->let g: int = 1_000_000; +>let sum : tez = 100_000mutez >``` ```reasonligo group=a -(* int + int produces int *) -let a: int = 5 + 10; -(* nat + int produces int *) -let b: int = 5n + 10; -(* tez + tez produces tez *) -let c: tez = 5mutez + 10mutez; -(* you can't add tez + int or tez + nat, this won't compile: - let d: tez = 5mutez + 10n; *) -(* two nats produce a nat *) -let e: nat = 5n + 10n; -(* nat + int produces an int, this won't compile: -let f: nat = 5n + 10; *) -let g: int = 1_000_000; +// int + int yields int +let a : int = 5 + 10; + +// nat + int yields int +let b : int = 5n + 10; + +// tez + tez yields tez +let c : tez = 5mutez + 10mutez; + +// tez + int or tez + nat is invalid: +// let d : tez = 5mutez + 10n; + +// two nats yield a nat +let e : nat = 5n + 10n; + +// nat + int yields an int: invalid +//let f : nat = 5n + 10; + +let g : int = 1_000_000; ``` -> Pro tip: you can use underscores for readability when defining large numbers -> +> Pro tip: you can use underscores for readability when defining large +> numbers: >```reasonligo ->let g: int = 1_000_000; +>let sum : tex = 100_000mutez; >``` ## Subtraction -The simpliest substraction looks like this: +Subtraction looks like as follows. > ⚠️ Even when subtracting two `nats`, the result is an `int` ```pascaligo group=b -const a: int = 5 - 10; -// substraction of two nats, yields an int -const b: int = 5n - 2n; -// won't compile, result is an int, not a nat -// const c: nat = 5n - 2n; -const d: tez = 5mutez - 1mutez; +const a : int = 5 - 10 + +// Subtraction of two nats yields an int +const b : int = 5n - 2n + +// Therefore the following is invalid +// const c : nat = 5n - 2n + +const d : tez = 5mutez - 1mutez ``` ```cameligo group=b -let a: int = 5 - 10 -// substraction of two nats, yields an int -let b: int = 5n - 2n -// won't compile, result is an int, not a nat -// const c: nat = 5n - 2n -let d: tez = 5mutez - 1mutez +let a : int = 5 - 10 + +// Subtraction of two nats yields an int +let b : int = 5n - 2n + +// Therefore the following is invalid +// const c : nat = 5n - 2n + +let d : tez = 5mutez - 1mutez ``` ```reasonligo group=b -let a: int = 5 - 10; -(* substraction of two nats, yields an int *) -let b: int = 5n - 2n; -(* won't compile, result is an int, not a nat *) -(* let c: nat = 5n - 2n; *) -let d: tez = 5mutez - 1mutez; +let a : int = 5 - 10; + +// Subtraction of two nats yields an int +let b : int = 5n - 2n; + +// Therefore the following is invalid +// let c : nat = 5n - 2n; + +let d : tez = 5mutez - 1mutez; ``` @@ -134,88 +170,106 @@ You can multiply values of the same type, such as: ```pascaligo group=c -const a: int = 5 * 5; -const b: nat = 5n * 5n; -// you can also multiply `nat` and `tez` -const c: tez = 5n * 5mutez; +const a : int = 5 * 5 +const b : nat = 5n * 5n +// You can also multiply `nat` and `tez` in any order +const c : tez = 5n * 5mutez; ``` ```cameligo group=c -let a: int = 5 * 5 -let b: nat = 5n * 5n -// you can also multiply `nat` and `tez` -let c: tez = 5n * 5mutez +let a : int = 5 * 5 +let b : nat = 5n * 5n +// You can also multiply `nat` and `tez` in any order +let c : tez = 5n * 5mutez ``` ```reasonligo group=c -let a: int = 5 * 5; -let b: nat = 5n * 5n; -(* you can also multiply `nat` and `tez` *) -let c: tez = 5n * 5mutez; +let a : int = 5 * 5; +let b : nat = 5n * 5n; +// You can also multiply `nat` and `tez` in any order +let c : tez = 5n * 5mutez; ``` - ## Division -In LIGO you can divide `int`, `nat`, and `tez`. Here's how: +In LIGO you can divide `int`, `nat`, and `tez`. Here is how: > ⚠️ Division of two `tez` values results into a `nat` ```pascaligo group=d -const a: int = 10 / 3; -const b: nat = 10n / 3n; -const c: nat = 10mutez / 3mutez; +const a : int = 10 / 3 +const b : nat = 10n / 3n +const c : nat = 10mutez / 3mutez ``` ```cameligo group=d -let a: int = 10 / 3 -let b: nat = 10n / 3n -let c: nat = 10mutez / 3mutez +let a : int = 10 / 3 +let b : nat = 10n / 3n +let c : nat = 10mutez / 3mutez ``` ```reasonligo group=d -let a: int = 10 / 3; -let b: nat = 10n / 3n; -let c: nat = 10mutez / 3mutez; +let a : int = 10 / 3; +let b : nat = 10n / 3n; +let c : nat = 10mutez / 3mutez; ``` ## From `int` to `nat` and back -You can *cast* an `int` to a `nat` and vice versa, here's how: +You can *cast* an `int` to a `nat` and vice versa. Here is how: - + ```pascaligo group=e -const a: int = int(1n); -const b: nat = abs(1); +const a : int = int (1n) +const b : nat = abs (1) +``` + + +```cameligo group=e +let a : int = int (1n) +let b : nat = abs (1) +``` + + +```reasonligo group=e +let a : int = int (1n); +let b : nat = abs (1); ``` ## Check if a value is a `nat` -You can check if a value is a `nat`, by using a syntax specific built-in function, which accepts an `int` and returns an `option(nat)`, more specifically `Some(nat)` if the provided integer is a natural number, and `None` otherwise: +You can check if a value is a `nat` by using a syntax specific +built-in function, which accepts an `int` and returns an optional +`nat`: if `Some(nat)` then the provided integer was indeed a natural +number, and not otherwise. - -```pascaligo -const its_a_nat: option(nat) = is_nat(1) + +```pascaligo group=e +const is_a_nat : option (nat) = is_nat (1) +``` + + +```cameligo group=e +let is_a_nat : nat option = Michelson.is_nat (1) ``` ```reasonligo group=e -let a: int = int(1n); -let b: nat = abs(1); +let is_a_nat : option (nat) = Michelson.is_nat (1); ``` - \ No newline at end of file + diff --git a/gitlab-pages/docs/language-basics/sets-lists-tuples.md b/gitlab-pages/docs/language-basics/sets-lists-tuples.md index bc05f0ce3..330187ac3 100644 --- a/gitlab-pages/docs/language-basics/sets-lists-tuples.md +++ b/gitlab-pages/docs/language-basics/sets-lists-tuples.md @@ -1,329 +1,79 @@ --- id: sets-lists-tuples -title: Sets, Lists, Tuples +title: Tuples, Lists, Sets --- 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*. - -### Defining a set - - - -```pascaligo group=a -type int_set is set (int); -const my_set : int_set = set 1; 2; 3 end -``` - - -```cameligo group=a -type int_set = int set -let my_set : int_set = - Set.add 3 (Set.add 2 (Set.add 1 (Set.empty: int set))) -``` - - -```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)))); -``` - - - -### Empty sets - - - -```pascaligo group=a -const my_set: int_set = set end -const my_set_2: int_set = set_empty -``` - -```cameligo group=a -let my_set: int_set = (Set.empty: int set) -``` - -```reasonligo group=a -let my_set: int_set = (Set.empty: set (int)); -``` - - -### Checking if set contains an element - - - -```pascaligo group=a -const contains_three : bool = my_set contains 3 -// or alternatively -const contains_three_fn: bool = set_mem (3, my_set); -``` - - -```cameligo group=a -let contains_three: bool = Set.mem 3 my_set -``` - -```reasonligo group=a -let contains_three: bool = Set.mem(3, my_set); -``` - - - - -### Obtaining the size of a set - - -```pascaligo group=a -const set_size: nat = size (my_set) -``` - - -```cameligo group=a -let set_size: nat = Set.size my_set -``` - - -```reasonligo group=a -let set_size: nat = Set.size (my_set); -``` - - - - -### Modifying a set - - -```pascaligo group=a -const larger_set: int_set = set_add(4, my_set); -const smaller_set: int_set = set_remove(3, my_set); -``` - - - -```cameligo group=a -let larger_set: int_set = Set.add 4 my_set -let smaller_set: int_set = Set.remove 3 my_set -``` - - - -```reasonligo group=a -let larger_set: int_set = Set.add(4, my_set); -let smaller_set: int_set = Set.remove(3, my_set); -``` - - - - -### Folding a set - - -```pascaligo group=a -function sum(const result: int; const i: int): int is result + i; -// Outputs 6 -const sum_of_a_set: int = set_fold(sum, my_set, 0); -``` - - -```cameligo group=a -let sum (result, i: int * int) : int = result + i -let sum_of_a_set: int = Set.fold sum my_set 0 -``` - - -```reasonligo group=a -let sum = (result_i: (int, int)): int => result_i[0] + result_i[1]; -let sum_of_a_set: int = Set.fold(sum, my_set, 0); -``` - - -## Lists - -Lists are similar to sets, but their elements don't need to be unique and they don't offer the same range of built-in functions. - -> 💡 Lists are useful when returning operations from a smart contract's entrypoint. - -### Defining a list - - - -```pascaligo group=b -type int_list is list(int); -const my_list: int_list = list - 1; - 2; - 3; -end -``` - - -```cameligo group=b -type int_list = int list -let my_list: int_list = [1; 2; 3] -``` - - -```reasonligo group=b -type int_list = list(int); -let my_list: int_list = [1, 2, 3]; -``` - - - - -### Appending an element to a list - - - -```pascaligo group=b -const larger_list: int_list = cons(4, my_list); -const even_larger_list: int_list = 5 # larger_list; -``` - - -```cameligo group=b -let larger_list: int_list = 4 :: my_list -(* CameLIGO doesn't have a List.cons *) -``` - - -```reasonligo group=b -let larger_list: int_list = [4, ...my_list]; -(* ReasonLIGO doesn't have a List.cons *) -``` - - - -
-> 💡 Lists can be iterated, folded or mapped to different values. You can find additional examples [here](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts) and other built-in operators [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L59) - -### Mapping of a list - - - -```pascaligo group=b -function increment(const i: int): int is block { skip } with i + 1; -// Creates a new list with elements incremented by 1 -const incremented_list: int_list = list_map(increment, even_larger_list); -``` - - - -```cameligo group=b -let increment (i: int) : int = i + 1 -(* Creates a new list with elements incremented by 1 *) -let incremented_list: int_list = List.map increment larger_list -``` - - - - -```reasonligo group=b -let increment = (i: int): int => i + 1; -(* Creates a new list with elements incremented by 1 *) -let incremented_list: int_list = List.map(increment, larger_list); -``` - - - - -### Folding of a list: - - -```pascaligo group=b -function sum(const result: int; const i: int): int is block { skip } with result + i; -// Outputs 6 -const sum_of_a_list: int = list_fold(sum, my_list, 0); -``` - - - -```cameligo group=b -let sum (result, i: int * int) : int = result + i -// Outputs 6 -let sum_of_a_list: int = List.fold sum my_list 0 -``` - - - -```reasonligo group=b -let sum = ((result, i): (int, int)): int => result + i; -(* Outputs 6 *) -let sum_of_a_list: int = List.fold(sum, my_list, 0); -``` - - +exposes `tuples`, `lists` and `sets`. +> ⚠️ Make sure to pick the appropriate data type for your use case, and +> bear in mind the related gas costs. ## Tuples -Tuples are used to store related data that has a **specific order** and **defined -length** without the need for named fields or a dedicated type identity. Probably -the most common tuple is a pair of type `(a, b)`. For example, if we were storing -coordinates on a two dimensional grid we might use a pair tuple of type `int * int` -to store the coordinates x and y. There is a **specific order** because x and y must -always stay in the same location within the tuple for the data to make sense. There is -also a **defined length** because the tuple pair can only ever have two elements, -if we added a third dimension `z` its type would be incompatible with that of the -pair tuple. +Tuples gather a given number of values in a specific order and those +values, called *components*, can be retrieved by their index +(position). Probably the most common tuple is the *pair*. For +example, if we were storing coordinates on a two dimensional grid we +might use a pair of type `int * int` to store the coordinates `x` and +`y` as the pair value `(x,y)`. There is a *specific order*, so `(y,x)` +is not equal to `(x,y)`. The number of components is part of the type +of a tuple, so, for example, we cannot add an extra component to a +pair and obtain a triple of the same type: `(x,y)` has always a +different type from `(x,y,z)`, whereas `(y,x)` may have the same type. -Like records, tuples can have members of arbitrary types in the same structure. +Like records, tuple components can be of arbitrary types. ### Defining a tuple -Unlike [a record](language-basics/maps-records.md), tuple types do not have to be -defined before they can be used. However below we will give them names for the -sake of illustration. +Unlike [a record](language-basics/maps-records.md), tuple types do not +have to be defined before they can be used. However below we will give +them names by *type aliasing*. ```pascaligo group=c -type full_name is string * string; -const full_name: full_name = ("Alice", "Johnson"); +type full_name is string * string // Alias + +const full_name : full_name = ("Alice", "Johnson") ``` ```cameligo group=c -type full_name = string * string +type full_name = string * string // Alias + (* The parenthesis here are optional *) -let full_name: full_name = ("Alice", "Johnson") +let full_name : full_name = ("Alice", "Johnson") ``` ```reasonligo group=c -type full_name = (string, string); +type full_name = (string, string); // Alias + (* The parenthesis here are optional *) -let full_name: full_name = ("Alice", "Johnson"); +let full_name : full_name = ("Alice", "Johnson"); ``` -### Accessing an element in a tuple +### Accessing an Element in a Tuple -The traditional way to access the elements of a tuple in OCaml is through -[a pattern match](language-basics/unit-option-pattern-matching.md). LIGO **does -not** currently support tuple patterns in its syntaxes. - -However, it is possible to access LIGO tuples by their position. +Accessing the components of a tuple in OCaml is achieved by +[pattern matching](language-basics/unit-option-pattern-matching.md). LIGO +currently supports tuple patterns only in the parameters of functions, +not in pattern matching. In LIGO, however, we can access components by +their position in their tuple, which cannot be done in OCaml. -Tuple elements are one-indexed and accessed like so: +Tuple components are one-indexed like so: ```pascaligo group=c -const first_name: string = full_name.1; +const first_name : string = full_name.1; ``` @@ -331,12 +81,426 @@ const first_name: string = full_name.1; Tuple elements are zero-indexed and accessed like so: ```cameligo group=c -let first_name: string = full_name.0 +let first_name : string = full_name.0 ``` + +Tuple components are one-indexed like so: + ```reasonligo group=c -let first_name: string = full_name[1]; +let first_name : string = full_name[1]; +``` + +## Lists + +Lists are linear collections of elements of the same type. Linear +means that, in order to reach an element in a list, we must visit all +the elements before (sequential access). Elements can be repeated, as +only their order in the collection matters. The first element is +called the *head*, and the sub-list after the head is called the +*tail*. For those familiar with algorithmic data structure, you can +think of a list a *stack*, where the top is written on the left. + +> 💡 Lists are useful when returning operations from a smart +> contract's entrypoint. + +### Defining a List + + + +```pascaligo group=b +const my_list : list (int) = list [1; 2; 2] // The head is 1 +``` + + +```cameligo group=b +let my_list : int list = [1; 2; 2] // The head is 1 +``` + + +```reasonligo group=b +let my_list : list (int) = [1, 2, 2]; // The head is 1 ``` + + +### Adding to a List + + + +Lists can be augmented by adding an element before the head (or, in +terms of stack, by *pushing an element on top*). This operation is +usually called *consing* in functional languages. + + + +In PascaLIGO, the *cons operator* is infix and noted `#`. It is not +symmetric: on the left lies the element to cons, and, on the right, a +list on which to cons. (The symbol is helpfully asymmetric to remind +you of that.) + +```pascaligo group=b +const larger_list : list (int) = 5 # my_list +``` + + + +In CameLIGO, the *cons operator* is infix and noted `::`. It is not +symmetric: on the left lies the element to cons, and, on the right, a +list on which to cons. + +```cameligo group=b +let larger_list : int list = 5 :: my_list +``` + + + +In ReasonLIGO, the *cons operator* is infix and noted `, ...`. It is +not symmetric: on the left lies the element to cons, and, on the +right, a list on which to cons. + +```reasonligo group=b +let larger_list : list (int) = [5, ...my_list]; +``` + + + +
+ +> 💡 Lists can be iterated, folded or mapped to different values. You +> can find additional examples +> [here](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts) +> and other built-in operators +> [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L59) + +### Mapping of a List + +We may want to apply a function to all the elements of a list and +obtain the resulting list, in the same order. For example, we may want +to create a list that contains all the elements of another list +incremented by one. This is a special case of *fold operation* called +a *map operation*. Map operations (not to be confused by the +[map data structure](language-basics/maps-records.md)), are predefined +functions in LIGO. They take as a parameter the function to apply to +all the elements. Of course, that function must return a value of the +same type as the element. + + + + + +In PascaLIGO, the map function is called `list_map`. + +```pascaligo group=b +function increment (const i : int): int is i + 1 + +// Creates a new list with all elements incremented by 1 +const plus_one : list (int) = list_map (increment, larger_list) +``` + + + +In CameLIGO, the map function is called `List.map`. + +```cameligo group=b +let increment (i : int) : int = i + 1 + +// Creates a new list with all elements incremented by 1 +let plus_one : int list = List.map increment larger_list +``` + + + +In CameLIGO, the map function is called `List.map`. + +```reasonligo group=b +let increment = (i : int) : int => i + 1; + +// Creates a new list with all elements incremented by 1 +let plus_one : list (int) = List.map (increment, larger_list); +``` + + + + +### Folding of over a List + + + +```pascaligo group=b +function sum (const acc : int; const i : int): int is acc + i + +const sum_of_elements : int = list_fold (sum, my_list, 0) +``` + + + +```cameligo group=b +let sum (acc, i: int * int) : int = acc + i +let sum_of_elements : int = List.fold sum my_list 0 +``` + + + +```reasonligo group=b +let sum = ((result, i): (int, int)): int => result + i; +let sum_of_elements : int = List.fold (sum, my_list, 0); +``` + + + + +## Sets + +Sets are unordered collections of values of the same type, like lists +are ordered collections. Like the mathematical sets and lists, sets +can be empty and, if not, elements of sets in LIGO are *unique*, +whereas they can be repeated in a list. + +### Empty Sets + + + +```pascaligo group=a +const my_set : set (int) = set [] +``` + +```cameligo group=a +let my_set : int set = (Set.empty : int set) +``` + +```reasonligo group=a +let my_set : set (int) = (Set.empty : set (int)); +``` + + +### Non-empty Sets + + + + + +In PascaLIGO, the notation for sets is similar to that for lists, +except the keyword `set` is used before: + +```pascaligo group=a +const my_set : set (int) = set [3; 2; 2; 1] +``` + +You can check that `2` is not repeated in `my_set` by using the LIGO +compiler like this (the output will sort the elements of the set, but +that order is not significant for the compiler): + +```shell +ligo evaluate-value +gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo my_set +# Outputs: { 3 ; 2 ; 1 } +``` + + + +In CameLIGO, there is no predefined syntactic construct for sets: you +must build your set by adding to the empty set. (This is the way in +OCaml.) + +```cameligo group=a +let my_set : int set = + Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set)))) +``` + +You can check that `2` is not repeated in `my_set` by using the LIGO +compiler like this (the output will sort the elements of the set, but +that order is not significant for the compiler): + +```shell +ligo evaluate-value +gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo my_set +# Outputs: { 3 ; 2 ; 1 } +``` + + + +In ReasonLIGO, there is no predefined syntactic construct for sets: +you must build your set by adding to the empty set. (This is the way +in OCaml.) + +```reasonligo group=a +let my_set : set (int) = + Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int))))); +``` + +You can check that `2` is not repeated in `my_set` by using the LIGO +compiler like this (the output will sort the elements of the set, but +that order is not significant for the compiler): + +```shell +ligo evaluate-value +gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo my_set +# Outputs: { 3 ; 2 ; 1 } +``` + + + +### Set Membership + + + + + +PascaLIGO features a special keyword `constains` that operates like an +infix operator checking membership in a set. + +```pascaligo group=a +const contains_3 : bool = my_set contains 3 +``` + + +```cameligo group=a +let contains_3 : bool = Set.mem 3 my_set +``` + + +```reasonligo group=a +let contains_3 : bool = Set.mem (3, my_set); +``` + + + + +### Cardinal + + + + +```pascaligo group=a +const set_size : nat = size (my_set) +``` + + +```cameligo group=a +let set_size : nat = Set.size my_set +``` + + +```reasonligo group=a +let set_size : nat = Set.size (my_set); +``` + + + + +### Adding or Removing from a Set + + + + + +In PascaLIGO, there are two ways to update a set. Either we create a +new set from the given one, or we modify it in-place. First, let us +consider the former: + +```pascaligo group=a +const larger_set : set (int) = set_add (4, my_set) + +const smaller_set : set (int) = set_remove (3, my_set) +``` + +If we are in a block, we can use an instruction to modify the set +bound to a given variable. This is called a *patch*. It is only +possible to add elements by means of a patch, not remove any: it is +the union of two sets. + +In the following example, the parameter set `s` of function `update` +is augmented (as the `with s` shows) to include `4` and `7`, that is, +this instruction is equivalent to perform the union of two sets, one +that is modified in-place, and the other given as a literal +(extensional definition). + +``pascaligo group=a +function update (var s : set (int)) : set (int) is block { + patch s with set [4; 7] +} with s + +const new_set : set (int) = update (my_set) +``` + + + +In CameLIGO, we update a given set by creating another one, with or +without some elements. + +```cameligo group=a +let larger_set : int set = Set.add 4 my_set + +let smaller_set : int set = Set.remove 3 my_set +``` + + + +In ReasonLIGO, we update a given set by creating another one, with or +without some elements. + +```reasonligo group=a +let larger_set : set (int) = Set.add (4, my_set); + +let smaller_set : set (int) = Set.remove (3, my_set); +``` + + + + +### Folding over a Set + + + +Given a set, we may want to apply a function in turn to all the +elements it contains, while accumulating some value which is returned +at the end. This is a *fold operation*. In the following example, we +sum up all the elements of the set `my_set` defined above. + + + +In PascaLIGO, the folded function takes the accumulator first and the +(current) set element second. The predefined fold is called `set_fold`. + +```pascaligo group=a +function sum (const acc : int; const i : int): int is acc + i + +const sum_of_elements : int = set_fold (sum, my_set, 0) +``` + +It is possible to use a *loop* over a set as well. + +```pascaligo group=a +function loop (const s : set (int)) : int is block { + var sum : int := 0; + for element in set s block { + sum := sum + element + } +} with sum +``` + + + +In CameLIGO, the predefined fold over sets is called `Set.fold`. + +```cameligo group=a +let sum (acc, i : int * int) : int = acc + i + +let sum_of_elements : int = Set.fold sum my_set 0 +``` + + +` +In ReasonLIGO, the predefined fold over sets is called `Set.fold`. + +``reasonligo group=a +let sum = ((acc, i) : (int, int)) : int => acc + i; + +let sum_of_elements : int = Set.fold (sum, my_set, 0); +``` + + + diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.ligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.ligo new file mode 100644 index 000000000..68f01d71b --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.ligo @@ -0,0 +1,4 @@ +type magnitude is Small | Large // See variant types + +function compare (const n : nat) : magnitude is + if n < 10n then Small (Unit) else Large (Unit) diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.mligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.mligo new file mode 100644 index 000000000..0a1f9d513 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.mligo @@ -0,0 +1,4 @@ +type magnitude = Small | Large // See variant types + +let compare (n : nat) : magnitude = + if n < 10n then Small else Large diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.religo b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.religo new file mode 100644 index 000000000..76bb0b7d2 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.religo @@ -0,0 +1,4 @@ +type magnitude = | Small | Large; // See variant types + +let compare = (n : nat) : magnitude => + if (n < 10n) { Small; } else { Large; }; diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/if-else.ligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/if-else.ligo deleted file mode 100644 index cc2b84529..000000000 --- a/gitlab-pages/docs/language-basics/src/boolean-if-else/if-else.ligo +++ /dev/null @@ -1,4 +0,0 @@ -const min_age: nat = 16n; - -function is_adult(const age: nat): bool is - if (age > min_age) then True else False \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/add.ligo b/gitlab-pages/docs/language-basics/src/functions/add.ligo deleted file mode 100644 index 9337b1668..000000000 --- a/gitlab-pages/docs/language-basics/src/functions/add.ligo +++ /dev/null @@ -1,4 +0,0 @@ -function add(const a: int; const b: int): int is - begin - const result: int = a + b; - end with result; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/anon.ligo b/gitlab-pages/docs/language-basics/src/functions/anon.ligo index b7f6f14f9..6018cf554 100644 --- a/gitlab-pages/docs/language-basics/src/functions/anon.ligo +++ b/gitlab-pages/docs/language-basics/src/functions/anon.ligo @@ -1,3 +1,4 @@ -const increment : (int -> int) = (function (const i : int) : int is i + 1); -// a = 2 -const a: int = increment(1); \ No newline at end of file +function increment (const b : int) : int is + (function (const a : int) : int is a + 1) (b) + +const a : int = increment (1); // a = 2 diff --git a/gitlab-pages/docs/language-basics/src/functions/anon.mligo b/gitlab-pages/docs/language-basics/src/functions/anon.mligo new file mode 100644 index 000000000..db47b030a --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/functions/anon.mligo @@ -0,0 +1,2 @@ +let increment (b : int) : int = (fun (a : int) -> a + 1) b +let a : int = increment 1 // a = 2 diff --git a/gitlab-pages/docs/language-basics/src/functions/anon.religo b/gitlab-pages/docs/language-basics/src/functions/anon.religo new file mode 100644 index 000000000..939723d3b --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/functions/anon.religo @@ -0,0 +1,2 @@ +let increment = (b : int) : int => ((a : int) : int => a + 1)(b); +let a : int = increment (1); // a = 2 diff --git a/gitlab-pages/docs/language-basics/src/functions/blockless.ligo b/gitlab-pages/docs/language-basics/src/functions/blockless.ligo index 6745c09e8..2ad367d58 100644 --- a/gitlab-pages/docs/language-basics/src/functions/blockless.ligo +++ b/gitlab-pages/docs/language-basics/src/functions/blockless.ligo @@ -1 +1 @@ -function add(const a: int; const b: int): int is a + b \ No newline at end of file +function add (const a: int; const b: int): int is a + b \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/blockless.mligo b/gitlab-pages/docs/language-basics/src/functions/blockless.mligo new file mode 100644 index 000000000..f9f861c0f --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/functions/blockless.mligo @@ -0,0 +1 @@ +let add (a : int) (b : int) : int = a + b diff --git a/gitlab-pages/docs/language-basics/src/functions/blockless.religo b/gitlab-pages/docs/language-basics/src/functions/blockless.religo new file mode 100644 index 000000000..dd9f8eb09 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/functions/blockless.religo @@ -0,0 +1 @@ +let add = ((a, b): (int, int)) : int => a + b; diff --git a/gitlab-pages/docs/language-basics/src/functions/curry.mligo b/gitlab-pages/docs/language-basics/src/functions/curry.mligo new file mode 100644 index 000000000..3de7b4164 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/functions/curry.mligo @@ -0,0 +1,3 @@ +let add (a, b : int * int) : int = a + b // Uncurried +let add_curry (a : int) (b : int) : int = add (a,b) // Curried +let increment (b : int) : int = add_curry 1 // Partial application diff --git a/gitlab-pages/docs/language-basics/src/loops/collection.ligo b/gitlab-pages/docs/language-basics/src/loops/collection.ligo new file mode 100644 index 000000000..cfca6d962 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/loops/collection.ligo @@ -0,0 +1,22 @@ +function sum_list (var l : list (int)) : int is block { + var total : int := 0; + for i in list l block { + total := total + i + } +} with total + +function sum_set (var s : set (int)) : int is block { + var total : int := 0; + for i in set s block { + total := total + i + } +} with total + +function sum_map (var m : map (string, int)) : string * int is block { + var string_total : string := ""; + var int_total : int := 0; + for key -> value in map m block { + string_total := string_total ^ key; + int_total := int_total + value + } +} with (string_total, int_total) diff --git a/gitlab-pages/docs/language-basics/src/loops/gcd.ligo b/gitlab-pages/docs/language-basics/src/loops/gcd.ligo new file mode 100644 index 000000000..176ab8def --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/loops/gcd.ligo @@ -0,0 +1,14 @@ +function gcd (var x : nat; var y : nat) : nat is block { + if x < y then + block { + const z : nat = x; + x := y; y := z + } + else skip; + var r : nat := 0n; + while y =/= 0n block { + r := x mod y; + x := y; + y := r + } +} with x diff --git a/gitlab-pages/docs/language-basics/src/loops/gcd.mligo b/gitlab-pages/docs/language-basics/src/loops/gcd.mligo new file mode 100644 index 000000000..4ec3924d1 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/loops/gcd.mligo @@ -0,0 +1,7 @@ +let iter (x,y : nat * nat) : bool * (nat * nat) = + if y = 0n then stop (x,y) else continue (y, x mod y) + +let gcd (x,y : nat * nat) : nat = + let x,y = if x < y then y,x else x,y in + let x,y = Loop.fold_while iter (x,y) + in x diff --git a/gitlab-pages/docs/language-basics/src/loops/gcd.religo b/gitlab-pages/docs/language-basics/src/loops/gcd.religo new file mode 100644 index 000000000..d201b8314 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/loops/gcd.religo @@ -0,0 +1,7 @@ +let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) => + if (y == 0n) { stop ((x,y)); } else { continue ((y, x mod y)); }; + +let gcd = ((x,y) : (nat, nat)) : nat => + let (x,y) = if (x < y) { (y,x); } else { (x,y); }; + let (x,y) = Loop.fold_while (iter, (x,y)); + x; diff --git a/gitlab-pages/docs/language-basics/src/loops/sum.ligo b/gitlab-pages/docs/language-basics/src/loops/sum.ligo new file mode 100644 index 000000000..58292ae81 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/loops/sum.ligo @@ -0,0 +1,6 @@ +function sum (var n : nat) : int is block { + var acc : int := 0; + for i := 1 to int (n) block { + acc := acc + i + } +} with acc \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_patch.ligo b/gitlab-pages/docs/language-basics/src/maps-records/record_patch.ligo new file mode 100644 index 000000000..5e673150d --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/maps-records/record_patch.ligo @@ -0,0 +1,10 @@ +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 { + patch p with record [x = p.x + vec.dx]; + patch p with record [y = p.y + vec.dy] + } with p diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_patch2.ligo b/gitlab-pages/docs/language-basics/src/maps-records/record_patch2.ligo new file mode 100644 index 000000000..c8666d95e --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/maps-records/record_patch2.ligo @@ -0,0 +1,9 @@ +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 { + patch p with record [x = p.x + vec.dx; y = p.y + vec.dy] + } with p diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_simu.ligo b/gitlab-pages/docs/language-basics/src/maps-records/record_simu.ligo new file mode 100644 index 000000000..0e7bb9f3d --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/maps-records/record_simu.ligo @@ -0,0 +1,8 @@ +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 { + const p : point = p with record [x = p.x + vec.dx; y = p.y + vec.dy] +} with p diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_update.ligo b/gitlab-pages/docs/language-basics/src/maps-records/record_update.ligo new file mode 100644 index 000000000..ddfc32981 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/maps-records/record_update.ligo @@ -0,0 +1,7 @@ +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 + p with record [x = p.x + vec.dx; y = p.y + vec.dy] diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_update.mligo b/gitlab-pages/docs/language-basics/src/maps-records/record_update.mligo new file mode 100644 index 000000000..9df753032 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/maps-records/record_update.mligo @@ -0,0 +1,7 @@ +type point = {x : int; y : int; z : int} +type vector = {dx : int; dy : int} + +let origin : point = {x = 0; y = 0; z = 0} + +let xy_translate (p, vec : point * vector) : point = + {p with x = p.x + vec.dx; y = p.y + vec.dy} diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_update.religo b/gitlab-pages/docs/language-basics/src/maps-records/record_update.religo new file mode 100644 index 000000000..d853367f9 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/maps-records/record_update.religo @@ -0,0 +1,7 @@ +type point = {x : int, y : int, z : int}; +type vector = {dx : int, dy : int}; + +let origin : point = {x : 0, y : 0, z : 0}; + +let xy_translate = ((p, vec) : (point, vector)) : point => + {...p, x : p.x + vec.dx, y : p.y + vec.dy}; diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/addition.ligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/addition.ligo deleted file mode 100644 index 690b22836..000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/addition.ligo +++ /dev/null @@ -1,13 +0,0 @@ -// int + int produces int -const a: int = 5 + 10; -// nat + int produces int -const b: int = 5n + 10; -// tez + tez produces tez -const c: tez = 5mutez + 10mutez; -// you can't add tez + int or tez + nat, this won't compile -// const d: tez = 5mutez + 10n; -// two nats produce a nat -const e: nat = 5n + 10n; -// nat + int produces an int, this won't compile -// const f: nat = 5n + 10; -const g: int = 1_000_000; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/casting.ligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/casting.ligo deleted file mode 100644 index a0a5c9f83..000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/casting.ligo +++ /dev/null @@ -1,2 +0,0 @@ -const a: int = int(1n); -const b: nat = abs(1); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/division.ligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/division.ligo deleted file mode 100644 index 622235e3e..000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/division.ligo +++ /dev/null @@ -1,5 +0,0 @@ -const a: int = 10 / 3; -const b: nat = 10n / 3n; -const c: nat = 10mutez / 3mutez; - -const d: int = 10 / 5 / 2 * 5; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/isnat.ligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/isnat.ligo deleted file mode 100644 index 5a89add1a..000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/isnat.ligo +++ /dev/null @@ -1 +0,0 @@ -const its_a_nat: option(nat) = is_nat(1) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/multiplication.ligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/multiplication.ligo deleted file mode 100644 index 95bdef4ab..000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/multiplication.ligo +++ /dev/null @@ -1,3 +0,0 @@ -const a: int = 5 * 5; -const b: nat = 5n * 5n; -const c: tez = 5n * 5mutez; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/substraction.ligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/substraction.ligo deleted file mode 100644 index cefcd4947..000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/substraction.ligo +++ /dev/null @@ -1,6 +0,0 @@ -const a: int = 5 - 10; -// substraction of two nats, yields an int -const b: int = 5n - 2n; -// won't compile, result is an int, not a nat -// const c: nat = 5n - 2n; -const d: tez = 5mutez - 1mutez; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-touples/empty-set.ligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/empty-set.ligo similarity index 100% rename from gitlab-pages/docs/language-basics/src/sets-lists-touples/empty-set.ligo rename to gitlab-pages/docs/language-basics/src/sets-lists-tuples/empty-set.ligo diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.ligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.ligo new file mode 100644 index 000000000..7d2d49fbc --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.ligo @@ -0,0 +1,12 @@ +const my_list : list (int) = list [1; 2; 2] // The head is 1 + +const larger_list : int_list = 5 # my_list + +function increment (const i : int): int is i + 1 + +// Creates a new list with all elements incremented by 1 +const plus_one : list (int) = list_map (increment, larger_list); + +function sum (const acc : int; const i : int): int is acc + i + +const sum_of_elements : int = list_fold (sum, my_list, 0) diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo new file mode 100644 index 000000000..b6ede8212 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo @@ -0,0 +1,28 @@ +type int_set is set (int) + +const my_set : int_set = set [3; 2; 2; 1] + +const contains_3 : bool = my_set contains 3 + +const set_size : nat = size (my_set) + +const larger_set : int_set = set_add (4, my_set) + +const smaller_set : int_set = set_remove (3, my_set) + +function update (var s : set (int)) : set (int) is block { + patch s with set [4; 7] +} with s + +const new_set : set (int) = update (my_set) + +function sum (const acc : int; const i : int): int is acc + i + +const sum_of_elements : int = set_fold (sum, my_set, 0) + +function loop (const s : set (int)) : int is block { + var sum : int := 0; + for element in set s block { + sum := sum + element + } +} with sum diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo new file mode 100644 index 000000000..9fd44a62e --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo @@ -0,0 +1,16 @@ +type int_set = int set + +let my_set : int_set = + Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set)))) + +let contains_3 : bool = Set.mem 3 my_set + +let set_size : nat = Set.size my_set + +let larger_set : int_set = Set.add 4 my_set + +let smaller_set : int_set = Set.remove 3 my_set + +let sum (acc, i : int * int) : int = acc + i + +let sum_of_elements : int = Set.fold sum my_set 0 diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo new file mode 100644 index 000000000..6727ed8d2 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo @@ -0,0 +1,16 @@ +type int_set = set (int); + +let my_set : int_set = + Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int))))); + +let contains_3 : bool = Set.mem (3, my_set); + +let set_size : nat = Set.size (my_set); + +let larger_set : int_set = Set.add (4, my_set); + +let smaller_set : int_set = Set.remove (3, my_set); + +let sum = ((acc, i) : (int, int)) : int => acc + i; + +let sum_of_elements : int = Set.fold (sum, my_set, 0); diff --git a/gitlab-pages/docs/language-basics/src/strings/concat.ligo b/gitlab-pages/docs/language-basics/src/strings/concat.ligo deleted file mode 100644 index 7b80e8510..000000000 --- a/gitlab-pages/docs/language-basics/src/strings/concat.ligo +++ /dev/null @@ -1 +0,0 @@ -const a: string = string_concat("Hello ", "World"); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/types/alias.ligo b/gitlab-pages/docs/language-basics/src/types/alias.ligo deleted file mode 100644 index d0b6414e6..000000000 --- a/gitlab-pages/docs/language-basics/src/types/alias.ligo +++ /dev/null @@ -1,2 +0,0 @@ -type animalBreed is string; -const dogBreed : animalBreed = "Saluki"; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/types/annotation.ligo b/gitlab-pages/docs/language-basics/src/types/annotation.ligo deleted file mode 100644 index 9373e08ec..000000000 --- a/gitlab-pages/docs/language-basics/src/types/annotation.ligo +++ /dev/null @@ -1,4 +0,0 @@ -type int_map is map(int, int); -function get_first(const int_map: int_map): option(int) is int_map[1] -// empty map needs a type annotation -const first: option(int) = get_first(((map end) : int_map )); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/types/composed-types.ligo b/gitlab-pages/docs/language-basics/src/types/composed-types.ligo deleted file mode 100644 index 3929e9de6..000000000 --- a/gitlab-pages/docs/language-basics/src/types/composed-types.ligo +++ /dev/null @@ -1,19 +0,0 @@ -// alias two types -type account is address; -type numberOfTransactions is nat; -// accountData consists of a record with two fields (balance, numberOfTransactions) -type accountData is record - balance: tez; - numberOfTransactions: numberOfTransactions; -end -// our ledger / accountBalances is a map of account <-> accountData -type accountBalances is map(account, accountData); - -// pseudo-JSON representation of our map -// { "tz1...": {balance: 10mutez, numberOfTransactions: 5n} } -const ledger: accountBalances = map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> record - balance = 10mutez; - numberOfTransactions = 5n; - end -end \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/types/simple-type.ligo b/gitlab-pages/docs/language-basics/src/types/simple-type.ligo index be96bd52a..c053e2eac 100644 --- a/gitlab-pages/docs/language-basics/src/types/simple-type.ligo +++ b/gitlab-pages/docs/language-basics/src/types/simple-type.ligo @@ -1,6 +1,7 @@ -// accountBalances is a simple type, a map of address <-> tez -type accountBalances is map(address, tez); +// The type accountBalances denotes maps from addresses to tez -const ledger: accountBalances = map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> 10mutez -end \ No newline at end of file +type account_balances is map (address, tez) + +const ledger : account_balances = + map + [("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez] diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo new file mode 100644 index 000000000..5f54bc61e --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo @@ -0,0 +1,7 @@ +type coin is Head | Tail + +function flip (const c : coin) : coin is + case c of + Head -> Tail (Unit) // Unit needed because of a bug + | Tail -> Head (Unit) // Unit needed because of a bug + end diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo index 148d7f2ee..d79db175e 100644 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo +++ b/gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo @@ -1,7 +1,4 @@ -// won't work, use const for global values instead -// var four: int = 4; - -function add(const a: int; const b: int) : int is - block { - var c : int := a + b; - } with c \ No newline at end of file +function add (const a : int; const b : int) : int is + block { + var c : int := a + b + } with c diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo new file mode 100644 index 000000000..d82e20eb5 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo @@ -0,0 +1,2 @@ +let add (a : int) (b : int) : int = + let c : int = a + b in c diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo b/gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo index 0cabb6def..0dace01b4 100644 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo +++ b/gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo @@ -1,4 +1,4 @@ -let add = (a: int, b: int): int => { +let add = (a: int, b: int) : int => { let c: int = a + b; c; -}; \ No newline at end of file +}; diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo new file mode 100644 index 000000000..878531d2d --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo @@ -0,0 +1 @@ +let age : int = 25 diff --git a/gitlab-pages/docs/language-basics/strings.md b/gitlab-pages/docs/language-basics/strings.md index 86e5e6b55..297edee09 100644 --- a/gitlab-pages/docs/language-basics/strings.md +++ b/gitlab-pages/docs/language-basics/strings.md @@ -3,22 +3,20 @@ id: strings title: Strings --- - - Strings are defined using the built-in `string` type like this: ``` -const a: string = "Hello Alice"; +const a : string = "Hello Alice" ``` ``` -let a: string = "Hello Alice" +let a : string = "Hello Alice" ``` ```reasonligo -let a: string = "Hello Alice"; +let a : string = "Hello Alice"; ``` @@ -30,77 +28,74 @@ let a: string = "Hello Alice"; Strings can be concatenated using the `^` operator. ```pascaligo -const name: string = "Alice"; -const greeting: string = "Hello"; -// Hello Alice -const full_greeting: string = greeting ^ " " ^ name; -// Hello Alice! (alternatively) -const full_greeting_exclamation: string = string_concat(full_greeting, "!"); +const name : string = "Alice" +const greeting : string = "Hello" +const full_greeting : string = greeting ^ " " ^ name ``` Strings can be concatenated using the `^` operator. ```cameligo -let name: string = "Alice" -let greeting: string = "Hello" -let full_greeting: string = greeting ^ " " ^ name +let name : string = "Alice" +let greeting : string = "Hello" +let full_greeting : string = greeting ^ " " ^ name ``` Strings can be concatenated using the `++` operator. ```reasonligo -let name: string = "Alice"; -let greeting: string = "Hello"; -let full_greeting: string = greeting ++ " " ++ name; +let name : string = "Alice"; +let greeting : string = "Hello"; +let full_greeting : string = greeting ++ " " ++ name; ``` ## Slicing strings -Strings can be sliced using the syntax specific built-in built-in function: +Strings can be sliced using a built-in function: ```pascaligo -const name: string = "Alice"; -// slice = "A" -const slice: string = string_slice(0n, 1n, name); +const name : string = "Alice" +const slice : string = string_slice (0n, 1n, name) ``` ```cameligo -let name: string = "Alice" -let slice: string = String.slice 0n 1n name +let name : string = "Alice" +let slice : string = String.slice 0n 1n name ``` ```reasonligo -let name: string = "Alice"; -let slice: string = String.slice(0n, 1n, name); +let name : string = "Alice"; +let slice : string = String.slice (0n, 1n, name); ``` -> ⚠️ Notice that the `offset` and slice `length` are `nats` +> ⚠️ Notice that the `offset` and slice `length` are natural numbers +> (`nat`). ## Aquiring the length of a string -The length of a string can be found using the syntax specific built-in function: +The length of a string can be found using a built-in function: ```pascaligo -const name: string = "Alice"; +const name : string = "Alice" // length = 5 -const length: nat = size(name); +const length : nat = size (name) ``` ```cameligo -let name: string = "Alice" -let length: nat = String.size name +let name : string = "Alice" +let length : nat = String.size name ``` ```reasonligo -let name: string = "Alice"; -let length: nat = String.size(name); +let name : string = "Alice"; +let length : nat = String.size (name); ``` diff --git a/gitlab-pages/docs/language-basics/tezos-specific.md b/gitlab-pages/docs/language-basics/tezos-specific.md index 5c9e0b1a6..a1f8c17dc 100644 --- a/gitlab-pages/docs/language-basics/tezos-specific.md +++ b/gitlab-pages/docs/language-basics/tezos-specific.md @@ -3,38 +3,45 @@ id: tezos-specific title: Tezos Domain-Specific Operations --- -LIGO is a language for writing Tezos smart contracts. It would be a little odd if -it didn't have any Tezos specific functions. This page will tell you about them. +LIGO is a programming language for writing Tezos smart contracts. It +would be a little odd if it did not have any Tezos specific +functions. This page will tell you about them. -## Pack and Unpack +## Pack and unpack -Michelson provides the `PACK` and `UNPACK` instructions for data serialization. -`PACK` converts Michelson data structures to a binary format, and `UNPACK` -reverses it. This functionality can be accessed from within LIGO. +Michelson provides the `PACK` and `UNPACK` instructions for data +serialization. The instruction `PACK` converts Michelson data +structures into a binary format, and `UNPACK` reverses that +transformation. This functionality can be accessed from within LIGO. -> ⚠️ `PACK` and `UNPACK` are features of Michelson that are intended to be used by people that really know what they're doing. There are several failure cases (such as `UNPACK`ing a lambda from an untrusted source), most of which are beyond the scope of this document. Don't use these functions without doing your homework first. +> ⚠️ `PACK` and `UNPACK` are Michelson instructions that are intended +> to be used by people that really know what they are doing. There are +> several risks and failure cases, such as unpacking a lambda from an +> untrusted source, and most of which are beyond the scope of this +> document. Do not use these functions without doing your homework +> first. -```pascaligo -function id_string (const p : string) : option(string) is block { - const packed : bytes = bytes_pack(p) ; -} with (bytes_unpack(packed): option(string)) +```pascaligo group=a +function id_string (const p : string) : option (string) is block { + const packed : bytes = bytes_pack (p) +} with (bytes_unpack (packed): option (string)) ``` -```cameligo -let id_string (p: string) : string option = +```cameligo group=a +let id_string (p : string) : string option = let packed: bytes = Bytes.pack p in - ((Bytes.unpack packed): string option) + (Bytes.unpack packed : string option) ``` -```reasonligo -let id_string = (p: string) : option(string) => { - let packed : bytes = Bytes.pack(p); - ((Bytes.unpack(packed)): option(string)); +```reasonligo group=a +let id_string = (p : string) : option (string) => { + let packed : bytes = Bytes.pack (p); + (Bytes.unpack(packed) : option (string)); }; ``` @@ -42,41 +49,35 @@ let id_string = (p: string) : option(string) => { ## Hashing Keys -It's often desirable to hash a public key. In Michelson, certain data structures -such as maps will not allow the use of the `key` type. Even if this weren't the case -hashes are much smaller than keys, and storage on blockchains comes at a cost premium. -You can hash keys with the `key_hash` type and associated built in function. +It is often desirable to hash a public key. In Michelson, certain data +structures such as maps will not allow the use of the `key` type. Even +if this were not the case, hashes are much smaller than keys, and +storage on blockchains comes at a cost premium. You can hash keys an +predefined function returning a value of type `key_hash`. -```pascaligo +```pascaligo group=b function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is block { - var ret : bool := False ; - var kh2 : key_hash := crypto_hash_key(k2) ; - if kh1 = kh2 then ret := True else skip; + var ret : bool := False; + var kh2 : key_hash := crypto_hash_key (k2); + if kh1 = kh2 then ret := True else skip } with (ret, kh2) ``` -```cameligo -let check_hash_key (kh1, k2: key_hash * key) : bool * key_hash = +```cameligo group=b +let check_hash_key (kh1, k2 : key_hash * key) : bool * key_hash = let kh2 : key_hash = Crypto.hash_key k2 in - if kh1 = kh2 - then (true, kh2) - else (false, kh2) + if kh1 = kh2 then true, kh2 else false, kh2 ``` -```reasonligo -let check_hash_key = ((kh1, k2): (key_hash, key)) : (bool, key_hash) => { +```reasonligo group=b +let check_hash_key = ((kh1, k2) : (key_hash, key)) : (bool, key_hash) => { let kh2 : key_hash = Crypto.hash_key(k2); - if (kh1 == kh2) { - (true, kh2); - } - else { - (false, kh2); - } + if (kh1 == kh2) { (true, kh2); } else { (false, kh2); } }; ``` @@ -84,60 +85,66 @@ let check_hash_key = ((kh1, k2): (key_hash, key)) : (bool, key_hash) => { ## Checking Signatures -Sometimes a contract will want to check that a message has been signed by a -particular key. For example, a point-of-sale system might want a customer to -sign a transaction so it can be processed asynchronously. You can do this in LIGO -using the `key` and `signature` types. +Sometimes a contract will want to check that a message has been signed +by a particular key. For example, a point-of-sale system might want a +customer to sign a transaction so it can be processed +asynchronously. You can do this in LIGO using the `key` and +`signature` types. -> ⚠️ There is no way to *generate* a signed message in LIGO. This is because that would require storing a private key on chain, at which point it isn't very private anymore. +> ⚠️ There is no way to *generate* a signed message in LIGO. This is +> because that would require storing a private key on chain, at which +> point it is not... private anymore. -```pascaligo +```pascaligo group=c function check_signature - (const pk: key; - const signed: signature; - const msg: bytes) : bool - is crypto_check(pk, signed, msg) + (const pk : key; + const signed : signature; + const msg : bytes) : bool + is crypto_check (pk, signed, msg) ``` -```cameligo -let check_signature (pk, signed, msg: key * signature * bytes) : bool = +```cameligo group=c +let check_signature (pk, signed, msg : key * signature * bytes) : bool = Crypto.check pk signed msg ``` -```reasonligo -let check_signature = ((pk, signed, msg): (key, signature, bytes)) : bool => { - Crypto.check(pk, signed, msg); +```reasonligo group=c +let check_signature = + ((pk, signed, msg) : (key, signature, bytes)) : bool => { + Crypto.check (pk, signed, msg); }; ``` - -## Getting The Contract's Own Address -Often you want to get the address of the contract being executed. You can do it with -`self_address`. +## Getting the contract's own address -> ⚠️ Due to limitations in Michelson, self_address in a contract is only allowed at the entry-point level. Using it in a utility function will cause an error. +Often you want to get the address of the contract being executed. You +can do it with `self_address`. + +> ⚠️ Due to limitations in Michelson, `self_address` in a contract is +> only allowed at the entry-point level (a.k.a top-level). Using it in +> an auxiliaru function will cause an error. -```pascaligo -const current_addr : address = self_address; +```pascaligo group=d +const current_addr : address = self_address ``` -```cameligo +```cameligo group=d let current_addr : address = Current.self_address ``` -```reasonligo +```reasonligo group=d let current_addr : address = Current.self_address; ``` diff --git a/gitlab-pages/docs/language-basics/types.md b/gitlab-pages/docs/language-basics/types.md index b19004a2b..6654e7a3b 100644 --- a/gitlab-pages/docs/language-basics/types.md +++ b/gitlab-pages/docs/language-basics/types.md @@ -3,163 +3,189 @@ id: types title: Types --- -LIGO is strongly and statically typed. This means that the compiler checks your program at compilation time and makes sure there won't be any type related runtime errors. LIGO types are built on top of Michelson's type system. +LIGO is strongly and statically typed. This means that the compiler +checks your program at compilation time and, if it passes the tests, +this ensures that there will be no runtime error due to wrong +assumptions on the data. This is called *type checking*. + +LIGO types are built on top of Michelson's type system. ## Built-in types -For quick referrence, you can find all the built-in types [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L35). +For quick reference, you can find all the built-in types [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L35). ## Type aliases -Type aliasing is great for creating a readable / maintainable smart contract. One well typed type/variable is worth a thousand words. For example we can choose to *alias* a string as an animal breed - this will allow us to comunicate our intent with added clarity. +*Type aliasing* consists in renaming a given type, when the context +calls for a more precise name. This increases readability and +maintainability of your smart contracts. For example we can choose to +alias a string type as an animal breed - this will allow us to +comunicate our intent with added clarity. -```pascaligo -type animalBreed is string; -const dogBreed : animalBreed = "Saluki"; +```pascaligo group=a +type breed is string +const dog_breed : breed = "Saluki" ``` -```cameligo -type animal_breed = string -let dog_breed: animal_breed = "Saluki" +```cameligo group=a +type breed = string +let dog_breed : breed = "Saluki" ``` -```reasonligo -type animal_breed = string; -let dog_breed: animal_breed = "Saluki"; +```reasonligo group=a +type breed = string; +let dog_breed : breed = "Saluki"; ``` -> Types in LIGO are `structural`, which means that `animalBreed`/`animal_breed` and `string` are interchangable and are considered equal. +> The above type definitions are aliases, which means that `breed` and +> `string` are interchangable in all contexts. ## Simple types + -```pascaligo -// accountBalances is a simple type, a map of address <-> tez -type accountBalances is map(address, tez); +```pascaligo group=b +// The type accountBalances denotes maps from addresses to tez + +type account_balances is map (address, tez) + +const ledger : account_balances = + map + [("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez] -const ledger: accountBalances = map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> 10mutez -end ``` -```cameligo -// account_balances is a simple type, a map of address <-> tez +```cameligo group=b +// The type account_balances denotes maps from addresses to tez + type account_balances = (address, tez) map -let ledger: account_balances = Map.literal - [(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), 10mutez)] +let ledger : account_balances = + Map.literal + [(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), 10mutez)] ``` -```reasonligo -(* account_balances is a simple type, a map of address <-> tez *) -type account_balances = map(address, tez); +```reasonligo group=b +// The type account_balances denotes maps from addresses to tez + +type account_balances = map (address, tez); let ledger: account_balances = - Map.literal([ - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, 10mutez) - ]); + Map.literal + ([("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, 10mutez)]); ``` -## Composed types +## Structured types -Often contracts require complex data structures, which in turn require well-typed storage or functions to work with. LIGO offers a simple way to compose simple types into larger & more expressive composed types. +Often contracts require complex data structures, which in turn require +well-typed storage or functions to work with. LIGO offers a simple way +to compose simple types into *structured types*. -In the example below you can see the definition of data types for a ledger that keeps the balance and number of previous transactions for a given account. +The first of those structured types is the *record*, which aggregates +types as *fields* and index them with a *field name*. In the example +below you can see the definition of data types for a ledger that keeps +the balance and number of previous transactions for a given account. -```pascaligo -// alias two types -type account is address; -type numberOfTransactions is nat; -// accountData consists of a record with two fields (balance, numberOfTransactions) -type accountData is record - balance: tez; - numberOfTransactions: numberOfTransactions; -end -// our ledger / accountBalances is a map of account <-> accountData -type accountBalances is map(account, accountData); +```pascaligo group=c +// Type aliasing +type account is address +type number_of_transactions is nat -// pseudo-JSON representation of our map -// { "tz1...": {balance: 10mutez, numberOfTransactions: 5n} } -const ledger: accountBalances = map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> record - balance = 10mutez; - numberOfTransactions = 5n; - end -end +// The type account_data is a record with two fields. +type account_data is record [ + balance : tez; + transactions : number_of_transactions +] + +// A ledger is a map from accounts to account_data +type ledger is map (account, account_data) + +const my_ledger : ledger = map [ + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> + record [ + balance = 10mutez; + transactions = 5n + ] +] ``` -```cameligo -(* alias two types *) +```cameligo group=c +// Type aliasing type account = address type number_of_transactions = nat -(* account_data consists of a record with two fields (balance, number_of_transactions) *) +// The type account_data is a record with two fields. type account_data = { - balance: tez; - number_of_transactions: number_of_transactions; + balance : tez; + transactions : number_of_transactions } -(* our ledger / account_balances is a map of account <-> account_data *) -type account_balances = (account, account_data) map +// A ledger is a map from accounts to account_data +type ledger = (account, account_data) map -// pseudo-JSON representation of our map -// {"tz1...": {balance: 10mutez, number_of_transactions: 5n}} -let ledger: account_balances = Map.literal - [(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), - {balance = 10mutez; - number_of_transactions = 5n;} - )] +let my_ledger : ledger = Map.literal + [(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), + {balance = 10mutez; transactions = 5n})] ``` -```reasonligo -(* alias two types *) +```reasonligo group=c +// Type aliasing type account = address; type number_of_transactions = nat; -(* account_data consists of a record with two fields (balance, number_of_transactions) *) + +// The type account_data is a record with two fields. type account_data = { - balance: tez, - number_of_transactions, + balance : tez, + transactions : number_of_transactions }; -(* our ledger / account_balances is a map of account <-> account_data *) -type account_balances = map(account, account_data); -(* pseudo-JSON representation of our map - {"tz1...": {balance: 10mutez, number_of_transactions: 5n}} *) -let ledger: account_balances = +// A ledger is a map from accounts to account_data +type ledger = map (account, account_data); + +let my_ledger : ledger = Map.literal([ - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, - {balance: 10mutez, number_of_transactions: 5n}) - ]); - + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, + {balance: 10mutez, transactions: 5n})]); ``` +The structured types which are dual to records are the *variant types* +and they are described in the section about *pattern matching*. They +are dual because records are a product of types (types are bundled +into a record), whereas variant types are a sum of types (they are +exclusive to each other). + ## Annotations -In certain cases, type of an expression cannot be properly determined. This can be circumvented by annotating an expression with it's desired type, here's an example: +In certain cases, the type of an expression cannot be properly +inferred by the compiler. In order to help the type checke, you can +annotate an expression with its desired type. Here is an example: ```pascaligo -type int_map is map(int, int); -function get_first(const int_map: int_map): option(int) is int_map[1] -// empty map needs a type annotation -const first: option(int) = get_first(((map end) : int_map )); +type int_map is map (int, int) + +function get_first (const my_map : int_map): option (int) is my_map[1] + +// The empty map always needs a type annotation + +const first : option (int) = get_first (((map end) : int_map)) ``` - \ No newline at end of file + diff --git a/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md b/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md index 1bd8a4a7e..3243750cc 100644 --- a/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md +++ b/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md @@ -3,110 +3,162 @@ id: unit-option-pattern-matching title: Unit, Option, Pattern matching --- -Optionals are a programing pattern seen in OCaml. Since Michelson and LIGO are both inspired by OCaml, you'll have the *option* to use them in LIGO as well. +Optionals are a pervasive programing pattern in OCaml. Since Michelson +and LIGO are both inspired by OCaml, *optional types* are available in +LIGO as well. Similarly, OCaml features a *unit* type, and LIGO +features it as well. Both the option type and the unit types are +instances of a more general kind of types: *variant types* (sometimes +called *sum types*). -## Type unit +## The unit type -Units in Michelson or LIGO represent *for the lack of better words* - an empty/useless/not needed value. +The `unit` type in Michelson or LIGO is a predefined type that +contains only one value that carries no information. It is used when +no relevant information is required or produced. Here is how it used. -Here's how they're defined: - -> 💡 Units come in handy when we try pattern matching on custom variants below. +> 💡 Units come in handy when we try pattern matching on custom +> variants below. -```pascaligo -const n: unit = Unit; + +In PascaLIGO, the unique value of the `unit` type is `Unit`. +```pascaligo group=a +const n : unit = Unit ``` -```cameligo -let n: unit = () + +In CameLIGO, the unique value of the `unit` type is `()`, following +the OCaml convention. +```cameligo group=a +let n : unit = () ``` -```reasonligo -let n: unit = (); + +In ReasonLIGO, the unique value of the `unit` type is `()`, following +the OCaml convention. +```reasonligo group=a +let n : unit = (); ``` -## Variants +## Variant types -Variant is a user-defined or a built-in type (in case of optionals) that can be compared to Enum (from javascript). +A variant type is a user-defined or a built-in type (in case of +options) that defines a type by cases, so a value of a variant type is +either this, or that or... The simplest variant type is equivalent to +the enumerated types found in Java, C++, JavaScript etc. -Here's how to define a new variant type: +Here is how we define a coin as being either head or tail (and nothing +else): -```pascaligo +```pascaligo group=b +type coin is Head | Tail +const head : coin = Head (Unit) // Unit needed because of a bug +const tail : coin = Tail (Unit) // Unit needed because of a bug +``` + + +```cameligo group=b +type coin = Head | Tail +let head : coin = Head +let tail : coin = Tail +``` + + +```reasonligo group=b +type coin = | Head | Tail; +let head : coin = Head; +let tail : coin = Tail; +``` + + +The names `Head` and `Tail` in the definition of the type `coin` are +called *data constructors*, or *variants*. + +In general, it is interesting for variants to carry some information, +and thus go beyond enumerated types. In the following, we show how to +define different kinds of users of a system. + + + + +```pascaligo group=c type id is nat + type user is -| Admin of id + Admin of id | Manager of id +| Guest + +const u : user = Admin (1000n) +const g : user = Guest (Unit) // Unit needed because of a bug +``` + + +```cameligo group=c +type id = nat + +type user = + Admin of id +| Manager of id +| Guest + +let u : user = Admin 1000n +let g : user = Guest +``` + + +```reasonligo group=c +type id = nat; + +type user = +| Admin (id) +| Manager (id) | Guest; -const u: user = Admin(1000n); -const g: user = Guest(Unit); -``` - - -```cameligo -type id = nat -type user = -| Admin of id -| Manager of id -| Guest of unit - -let u: user = Admin 1000n -let g: user = Guest () -``` - - -```reasonligo -type id = nat; -type user = - | Admin(id) - | Manager(id) - | Guest(unit); - -let u: user = Admin(1000n); -let g: user = Guest(); +let u : user = Admin (1000n); +let g : user = Guest; ``` -Defining a varient can be extremely useful for building semantically appealing contracts. We'll learn how to use variants for 'logic purposes' shortly. +Defining a variant can be extremely useful for building semantically +appealing contracts. We will learn how to use variants for "logic +purposes"' shortly. ## Optional values -Optionals are a type of built-in variant that can be used to determine if a variable holds a certain value or not. This is especially useful when (for example) your program's state allows for a certain variable value to be empty, like this: +The `option` type is a predefined variant type that is used to express +whether there is a value of some type or none. This is especially +useful when calling a *partial function*, that is, a function that is +not defined for some inputs. In that case, the value of the `option` +type would be `None`, otherwise `Some (v)`, where `v` is some +meaningful value *of any type*. An example in arithmetic is the +division operation: -```pascaligo -type dinner is option(string); - -// stay hungry -const p1: dinner = None; -// have some hamburgers -const p2: dinner = Some("Hamburgers") +```pascaligo group=d +function div (const a : nat; const b : nat) : option (nat) is + if b = 0n then (None: option (nat)) else Some (a/b) ``` -```cameligo -type dinner = string option - -let p1: dinner = None -let p2: dinner = Some "Hamburgers" +```cameligo group=d +let div (a, b : nat * nat) : nat option = + if b = 0n then (None: nat option) else Some (a/b) ``` -```reasonligo -type dinner = option(string); - -let p1: dinner = None; -let p2: dinner = Some("Hamburgers"); +```reasonligo group=d +let div = ((a, b) : (nat, nat)) : option (nat) => + if (b == 0n) { (None: option (nat)); } else { Some (a/b); }; ``` @@ -114,38 +166,66 @@ let p2: dinner = Some("Hamburgers"); ## Pattern matching -Pattern matching is very similiar to e.g. `switch` in Javascript, and can be used to re-route the program's flow based on a value of a variant. +*Pattern matching* is similiar to the `switch` construct in +Javascript, and can be used to route the program's control flow based +on the value of a variant. Consider for example the definition of a +function `flip` that flips a coin, as defined above. -```pascaligo -type dinner is option(string); -function is_hungry(const dinner: dinner): bool is block { skip } - with ( - case dinner of - | None -> True - | Some(d) -> False - end - ) +```pascaligo group=e +type coin is Head | Tail + +function flip (const c : coin) : coin is + case c of + Head -> Tail (Unit) // Unit needed because of a bug + | Tail -> Head (Unit) // Unit needed because of a bug + end +``` + +You can call the function `flip` by using the LIGO compiler like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo +flip "(Head (Unit))" +# Outputs: Tail(Unit) ``` -```cameligo -type dinner = string option -let is_hungry (d: dinner) : bool = - match d with - | None -> true - | Some s -> false +```cameligo group=e +type coin = Head | Tail + +let flip (c : coin) : coin = + match c with + Head -> Tail + | Tail -> Head +``` + +You can call the function `flip` by using the LIGO compiler like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.mligo +flip Head +# Outputs: Tail(Unit) ``` -```reasonligo -type dinner = option(string); -let is_hungry = (d: dinner): bool => - switch (d) { - | None => true - | Some(s) => false +```reasonligo group=e +type coin = | Head | Tail; + +let flip = (c : coin) : coin => + switch (c) { + | Head => Tail + | Tail => Head }; ``` +You can call the function `flip` by using the LIGO compiler like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.religo +flip Head +# Outputs: Tail(Unit) +``` + diff --git a/gitlab-pages/docs/language-basics/variables-and-constants.md b/gitlab-pages/docs/language-basics/variables-and-constants.md index 372f30180..87f2ab109 100644 --- a/gitlab-pages/docs/language-basics/variables-and-constants.md +++ b/gitlab-pages/docs/language-basics/variables-and-constants.md @@ -3,43 +3,48 @@ id: constants-and-variables title: Constants & Variables --- -The next building block after types are constants and variables. +The next building block after types are *constants* and *variables*. ## Constants -Constants are immutable by design, which means their values can't be reassigned. -When defining a constant you need to provide a `name`, `type` and a `value`: +Constants are immutable by design, which means their values cannot be +reassigned. Put in another way, they can be assigned once, at their +declaration. When defining a constant you need to provide a `name`, +`type` and a `value`: -```pascaligo -const age : int = 25; +```pascaligo group=a +const age : int = 25 ``` -You can evaluate the constant definition above using the following CLI command: +You can evaluate the constant definition above using the following CLI +command: ```shell -ligo evaluate-value -s pascaligo gitlab-pages/docs/language-basics/src/variables-and-constants/const.ligo age +ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.ligo age # Outputs: 25 ``` -```cameligo -let age: int = 25 +```cameligo group=a +let age : int = 25 ``` -You can evaluate the constant definition above using the following CLI command: +You can evaluate the constant definition above using the following CLI +command: ```shell -ligo evaluate-value -s cameligo gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo age +ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo age # Outputs: 25 ``` -```reasonligo -let age: int = 25; +```reasonligo group=a +let age : int = 25; ``` -You can evaluate the constant definition above using the following CLI command: +You can evaluate the constant definition above using the following CLI +command: ```shell -ligo evaluate-value -s reasonligo gitlab-pages/docs/language-basics/src/variables-and-constants/const.religo age +ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.religo age # Outputs: 25 ``` @@ -50,74 +55,78 @@ ligo evaluate-value -s reasonligo gitlab-pages/docs/language-basics/src/variable -Variables, unlike constants, are mutable. They can't be used in a *global scope*, but they can be used within functions, or function arguments. +Variables, unlike constants, are mutable. They cannot be declared in a +*global scope*, but they can be declared and used within functions, or +as function parameters. -> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs. +> 💡 Do not worry if you do not understand the function syntax yet. We +> will get to it in upcoming sections of this documentation. -> ⚠️ Please be wary that mutation only works within the function scope itself, values outside of the function scope will not be affected. +> ⚠️ Please be wary that mutation only works within the function scope +> itself, values outside of the function scope will not be affected. +```pascaligo group=b +// The following is invalid: use `const` for global values instead. +// var four : int = 4 -```pascaligo -// won't work, use const for global values instead -// var four: int = 4; - -function add(const a: int; const b: int) : int is - block { - var c : int := a + b; - } with c +function add (const a : int; const b : int) : int is + block { + var c : int := a + b + } with c ``` +> ⚠️ Notice the assignment operator `:=` for `var`, instead of `=` for +> constants. -> ⚠️ Notice the different assignment operator `:=` - -You can run the `add` function defined above using the LIGO compiler like this: +You can run the `add` function defined above using the LIGO compiler +like this: ```shell -ligo run-function -s pascaligo gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo add '(1,1)' +ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo add '(1,1)' # Outputs: 2 ``` -As expected from a functional language, CameLIGO uses value-binding -for variables rather than assignment. Variables are changed by replacement, -with a new value being bound in place of the old one. +As expected in the pure subset of a functional language, CameLIGO only +features constant values: once they are declared, the value cannot be +changed (or "mutated"). -> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs. +> 💡 Do not worry if you do not understand the function syntax yet. We +> will get to it in upcoming sections of this documentation. -```cameligo - -let add (a: int) (b: int) : int = +```cameligo group=c +let add (a : int) (b : int) : int = let c : int = a + b in c ``` -You can run the `add` function defined above using the LIGO compiler like this: - +You can run the `add` function defined above using the LIGO compiler +like this: ```shell -ligo run-function -s cameligo gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo add '(1,1)' +ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo add '(1,1)' # Outputs: 2 ``` -As expected from a functional language, ReasonLIGO uses value-binding -for variables rather than assignment. Variables are changed by replacement, -with a new value being bound in place of the old one. +As expected in the pure subset of a functional language, ReasonLIGO +only features constant values: once they are declared, the value +cannot be changed (or "mutated"). -> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs. +> 💡 Do not worry if you do not understand the function syntax yet. We +> will get to it in upcoming sections of this documentation. -```reasonligo - -let add = ((a,b): (int, int)): int => { - let c: int = a + b; +```reasonligo group=c +let add = ((a, b): (int, int)): int => { + let c : int = a + b; c; }; ``` -You can run the `add` function defined above using the LIGO compiler like this: - +You can run the `add` function defined above using the LIGO compiler +like this: ```shell -ligo run-function -s reasonligo gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo add '(1,1)' +ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo add '(1,1)' # Outputs: 2 ``` diff --git a/src/bin/expect_tests/lexer_tests.ml b/src/bin/expect_tests/lexer_tests.ml index 561346f5e..f55d9a8cb 100644 --- a/src/bin/expect_tests/lexer_tests.ml +++ b/src/bin/expect_tests/lexer_tests.ml @@ -106,7 +106,7 @@ ligo: : Lexical error in file "negative_byte_sequence.religo", line 1, character run_ligo_bad [ "compile-contract" ; "../../test/lexer/reserved_name.ligo" ; "main" ] ; [%expect {| ligo: : Lexical error in file "reserved_name.ligo", line 1, characters 4-13: - Reserved name: arguments. + Reserved name: "arguments". Hint: Change the name. {} @@ -123,7 +123,7 @@ ligo: : Lexical error in file "reserved_name.ligo", line 1, characters 4-13: run_ligo_bad [ "compile-contract" ; "../../test/lexer/reserved_name.religo" ; "main" ] ; [%expect {| ligo: : Lexical error in file "reserved_name.religo", line 1, characters 4-7: - Reserved name: end. + Reserved name: "end". Hint: Change the name. {} @@ -140,7 +140,7 @@ ligo: : Lexical error in file "reserved_name.religo", line 1, characters 4-7: run_ligo_bad [ "compile-contract" ; "../../test/lexer/reserved_name.mligo" ; "main" ] ; [%expect {| ligo: : Lexical error in file "reserved_name.mligo", line 1, characters 4-10: - Reserved name: object. + Reserved name: "object". Hint: Change the name. {} From 3583f72cb43d9cdf0ab77590c875a52b7220d817 Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Thu, 6 Feb 2020 11:47:41 +0100 Subject: [PATCH 6/9] Improvements from Sander and JDP. --- .../docs/advanced/entrypoints-contracts.md | 5 +-- .../docs/advanced/timestamps-addresses.md | 16 ++++----- .../docs/language-basics/boolean-if-else.md | 9 +++-- .../docs/language-basics/functions.md | 14 ++++---- gitlab-pages/docs/language-basics/loops.md | 24 +++++++------- .../docs/language-basics/maps-records.md | 33 +++++++++---------- .../docs/language-basics/math-numbers-tez.md | 11 +++---- .../unit-option-pattern-matching/flip.mligo | 6 ++++ .../unit-option-pattern-matching/flip.religo | 7 ++++ 9 files changed, 71 insertions(+), 54 deletions(-) create mode 100644 gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.mligo create mode 100644 gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.religo diff --git a/gitlab-pages/docs/advanced/entrypoints-contracts.md b/gitlab-pages/docs/advanced/entrypoints-contracts.md index c105d08f6..29160031c 100644 --- a/gitlab-pages/docs/advanced/entrypoints-contracts.md +++ b/gitlab-pages/docs/advanced/entrypoints-contracts.md @@ -386,13 +386,14 @@ type return = (list (operation), storage); let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address); -let proxy = ((param, store): (parameter, storage)) : return => +let proxy = ((param, store): (parameter, storage)) : return => { let counter : contract (parameter) = Operation.get_contract (dest); (* Reuse the parameter in the subsequent transaction or use another one, `mock_param`. *) let mock_param : parameter = Increment (5n); let op : operation = Operation.transaction (param, 0mutez, counter); - ([op], store); + ([op], store) +}; ``` diff --git a/gitlab-pages/docs/advanced/timestamps-addresses.md b/gitlab-pages/docs/advanced/timestamps-addresses.md index dd7289e81..8c20da011 100644 --- a/gitlab-pages/docs/advanced/timestamps-addresses.md +++ b/gitlab-pages/docs/advanced/timestamps-addresses.md @@ -101,8 +101,8 @@ let in_24_hrs : timestamp = today - one_day; ### Comparing Timestamps -You can also compare timestamps using the same comparison operators as -for numbers. +You can compare timestamps using the same comparison operators +applying to numbers. @@ -124,10 +124,10 @@ let not_tomorrow : bool = (Current.time == in_24_hrs); ## Addresses -The type `address` in LIGO is used to denote Tezos addresses (tz1, -tz2, tz3, KT1, ...). Currently, addresses are created by casting a -string to the type `address`. Beware of failures if the address is -invalid. Consider the following examples. +The type `address` in LIGO denotes Tezos addresses (tz1, tz2, tz3, +KT1, ...). Currently, addresses are created by casting a string to the +type `address`. Beware of failures if the address is invalid. Consider +the following examples. @@ -152,7 +152,7 @@ let my_account : address = ## Signatures -The type `signature` in LIGO datatype is used for Tezos signature +The `signature` type in LIGO datatype is used for Tezos signatures (edsig, spsig). Signatures are created by casting a string. Beware of failure if the signature is invalid. @@ -181,7 +181,7 @@ signature); ## Keys -The type `key` in LIGO is used for Tezos public keys. Do not confuse +The `key` type in LIGO is used for Tezos public keys. Do not confuse them with map keys. Keys are made by casting strings. Beware of failure if the key is invalid. diff --git a/gitlab-pages/docs/language-basics/boolean-if-else.md b/gitlab-pages/docs/language-basics/boolean-if-else.md index b6d3b0da3..d938f5b7b 100644 --- a/gitlab-pages/docs/language-basics/boolean-if-else.md +++ b/gitlab-pages/docs/language-basics/boolean-if-else.md @@ -34,7 +34,10 @@ In LIGO, only values of the same type can be compared. Moreover, not all values of the same type can be compared, only those with *comparable types*, which is a concept lifted from Michelson. Comparable types include, for instance, `int`, `nat`, -`string`, `tez`, `timestamp`, `address`, etc. +`string`, `tez`, `timestamp`, `address`, etc. As an example of +non-comparable types: maps, sets or lists are not comparable: if you +wish to compare them, you will have to write your own comparison +function. ### Comparing Strings @@ -110,7 +113,7 @@ let h : bool = (a != b); ```pascaligo group=d const a : tez = 5mutez const b : tez = 10mutez -const c : bool = (a = b) // false +const c : bool = (a = b) // False ``` ```cameligo group=d @@ -129,7 +132,7 @@ let c : bool = (a == b); // false ## Conditionals -Conditional logic enables to fork the control flow depending on the +Conditional logic enables forking the control flow depending on the state. diff --git a/gitlab-pages/docs/language-basics/functions.md b/gitlab-pages/docs/language-basics/functions.md index bcae8b468..c43e5e33e 100644 --- a/gitlab-pages/docs/language-basics/functions.md +++ b/gitlab-pages/docs/language-basics/functions.md @@ -10,11 +10,11 @@ logic into functions. ## Blocks In PascaLIGO, *blocks* enable the sequential composition of -*instructions* into an isolated scope. Each `block` needs to include -at least one instruction. If we need a placeholder *placeholder*, we -use the instruction called `skip`, that leaves the state -invariant. The rationale for `skip` instead of a truly empty block is -that it prevents you from writing an empty block by mistake. +instructions into an isolated scope. Each block needs to include at +least one instruction. If we need a placeholder, we use the +instruction `skip` which leaves the state unchanged. The rationale +for `skip` instead of a truly empty block is that it prevents you from +writing an empty block by mistake. @@ -31,10 +31,10 @@ Blocks are more versatile than simply containing instructions: they can also include *declarations* of values, like so: ```pascaligo skip // terse style -block { const a : int = 1; } +block { const a : int = 1 } // verbose style begin - const a : int = 1; + const a : int = 1 end ``` diff --git a/gitlab-pages/docs/language-basics/loops.md b/gitlab-pages/docs/language-basics/loops.md index 9a5a4d47b..6ae699cd6 100644 --- a/gitlab-pages/docs/language-basics/loops.md +++ b/gitlab-pages/docs/language-basics/loops.md @@ -64,7 +64,7 @@ to have a special type: if the type of the accumulator is `t`, then it must have the type `bool * t` (not simply `t`). It is the boolean value that denotes whether the stopping condition has been reached. -```cameligo group=b +```cameligo group=a let iter (x,y : nat * nat) : bool * (nat * nat) = if y = 0n then false, (x,y) else true, (y, x mod y) @@ -77,7 +77,7 @@ let gcd (x,y : nat * nat) : nat = To ease the writing and reading of the iterated functions (here, `iter`), two predefined functions are provided: `continue` and `stop`: -```cameligo group=c +```cameligo group=a let iter (x,y : nat * nat) : bool * (nat * nat) = if y = 0n then stop (x,y) else continue (y, x mod y) @@ -113,27 +113,29 @@ accumulator is `t`, then it must have the type `bool * t` (not simply `t`). It is the boolean value that denotes whether the stopping condition has been reached. -```reasonligo group=d +```reasonligo group=a let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) => if (y == 0n) { (false, (x,y)); } else { (true, (y, x mod y)); }; -let gcd = ((x,y) : (nat, nat)) : nat => +let gcd = ((x,y) : (nat, nat)) : nat => { let (x,y) = if (x < y) { (y,x); } else { (x,y); }; let (x,y) = Loop.fold_while (iter, (x,y)); - x; + x +}; ``` To ease the writing and reading of the iterated functions (here, `iter`), two predefined functions are provided: `continue` and `stop`: -```reasonligo group=e +```reasonligo group=b let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) => if (y == 0n) { stop ((x,y)); } else { continue ((y, x mod y)); }; -let gcd = ((x,y) : (nat, nat)) : nat => +let gcd = ((x,y) : (nat, nat)) : nat => { let (x,y) = if (x < y) { (y,x); } else { (x,y); }; let (x,y) = Loop.fold_while (iter, (x,y)); - x; + x +}; ``` @@ -147,7 +149,7 @@ To iterate over a range of integers you use a loop of the form `for familiar for programmers of imperative languages. Note that, for the sake of generality, the bounds are of type `int`, not `nat`. -```pascaligo group=f +```pascaligo group=c function sum (var n : nat) : int is block { var acc : int := 0; for i := 1 to int (n) block { @@ -177,7 +179,7 @@ of the form `for in Here is an example where the integers in a list are summed up. -```pascaligo group=g +```pascaligo group=d function sum_list (var l : list (int)) : int is block { var total : int := 0; for i in list l block { @@ -197,7 +199,7 @@ gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_list Here is an example where the integers in a set are summed up. -```pascaligo=g +```pascaligo=e function sum_set (var s : set (int)) : int is block { var total : int := 0; for i in set s block { diff --git a/gitlab-pages/docs/language-basics/maps-records.md b/gitlab-pages/docs/language-basics/maps-records.md index 6a6572c37..50ec7e8b8 100644 --- a/gitlab-pages/docs/language-basics/maps-records.md +++ b/gitlab-pages/docs/language-basics/maps-records.md @@ -358,7 +358,7 @@ let moves : register = let moves : register = Map.literal ([ ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)), - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3)),]); + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]); ``` > The `Map.literal` predefined function builds a map from a list of @@ -377,8 +377,8 @@ example: ```pascaligo group=f -(*const my_balance : option (move) = - moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] *) +const my_balance : option (move) = + moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] ``` @@ -418,11 +418,12 @@ let force_access (key, moves : address * register) : move = ``` -```reasonlig group=f -let force_access : ((key, moves) : address * register) : move => { - switch (Map.find_opt key moves) with - Some move -> move - | None -> (failwith "No move." : move) +```reasonligo group=f +let force_access = ((key, moves) : (address, register)) : move => { + switch (Map.find_opt (key, moves)) { + | Some (move) => move + | None => failwith ("No move.") : move + } }; ``` @@ -475,9 +476,8 @@ let assign (m : register) : register = ``` > 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 only -> defined on its key, but not its value. This encoding enables -> partially defined bindings. +> use `None` instead, that would have meant that the binding is +> removed. @@ -492,9 +492,8 @@ let assign = (m : register) : register => { ``` > 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 only -> defined on its key, but not its value. This encoding enables -> partially defined bindings. +> use `None` instead, that would have meant that the binding is +> removed. @@ -601,7 +600,7 @@ let map_op = (m : register) : register => { A *fold operation* is the most general of iterations. The iterated function takes two arguments: an *accumulator* and the structure *element* at hand, with which it then produces a new accumulator. This -enables to have a partial result that becomes complete when the +enables having a partial result that becomes complete when the traversal of the data structure is over. @@ -622,7 +621,7 @@ let fold_op (m : register) : register = ```reasonligo group=f -let fold_op = (m: register): register => { +let fold_op = (m : register) : register => { let aggregate = ((i,j): (int, (address, move))) => i + j[1][1]; Map.fold (aggregate, m, 5); }; @@ -703,7 +702,7 @@ let moves : register = let moves : register = Big_map.literal ([ ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)), - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3)),]); + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]); ``` > The predefind function `Big_map.literal` constructs a big map from a diff --git a/gitlab-pages/docs/language-basics/math-numbers-tez.md b/gitlab-pages/docs/language-basics/math-numbers-tez.md index 859c73097..bb274cd7c 100644 --- a/gitlab-pages/docs/language-basics/math-numbers-tez.md +++ b/gitlab-pages/docs/language-basics/math-numbers-tez.md @@ -8,15 +8,14 @@ LIGO offers three built-in numerical types: `int`, `nat` and `tez`. ## Addition Addition in LIGO is accomplished by means of the `+` infix -operator. Some type constraints apply, for example you ca not add a +operator. Some type constraints apply, for example you cannot add a value of type `tez` to a value of type `nat`. In the following example you can find a series of arithmetic operations, including various numerical types. However, some bits -remain in comments because they would otherwise not compile because, -for example, adding a value of type `int` to a value of type `tez` is -invalid. Note that adding an integer to a natural number produces an -integer. +remain in comments as they would otherwise not compile, for example, +adding a value of type `int` to a value of type `tez` is invalid. Note +that adding an integer to a natural number produces an integer. @@ -115,7 +114,7 @@ let g : int = 1_000_000; ## Subtraction -Subtraction looks like as follows. +Subtraction looks as follows. > ⚠️ Even when subtracting two `nats`, the result is an `int` diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.mligo new file mode 100644 index 000000000..43e09b016 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.mligo @@ -0,0 +1,6 @@ +type coin = Head | Tail + +let flip (c : coin) : coin = + match c with + Head -> Tail + | Tail -> Head diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.religo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.religo new file mode 100644 index 000000000..94d496c48 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.religo @@ -0,0 +1,7 @@ +type coin = | Head | Tail; + +let flip = (c : coin) : coin => + switch (c) { + | Head => Tail + | Tail => Head + }; From 9f9f5c82ae4abd47727108b87118766f94423df0 Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Thu, 6 Feb 2020 12:32:23 +0100 Subject: [PATCH 7/9] Added an example about lambdas. --- .../docs/language-basics/functions.md | 51 +++++++++++++++++++ .../src/functions/incr_map.ligo | 5 ++ .../src/functions/incr_map.mligo | 4 ++ .../src/functions/incr_map.religo | 4 ++ 4 files changed, 64 insertions(+) create mode 100644 gitlab-pages/docs/language-basics/src/functions/incr_map.ligo create mode 100644 gitlab-pages/docs/language-basics/src/functions/incr_map.mligo create mode 100644 gitlab-pages/docs/language-basics/src/functions/incr_map.religo diff --git a/gitlab-pages/docs/language-basics/functions.md b/gitlab-pages/docs/language-basics/functions.md index c43e5e33e..e68c93ed8 100644 --- a/gitlab-pages/docs/language-basics/functions.md +++ b/gitlab-pages/docs/language-basics/functions.md @@ -204,4 +204,55 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.religo # Outputs: 2 ``` + + +If the example above seems contrived, here is a more common design +pattern for lambdas: to be used as parameters to functions. Consider +the use case of having a list of integers and mapping the increment +function to all its elements. + + + +```pascaligo group=c +function incr_map (const l : list (int)) : list (int) is + list_map (function (const i : int) : int is i + 1, l) +``` +You can call the function `incr_map` defined above using the LIGO compiler +like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/functions/incr_map.ligo incr_map +"list [1;2;3]" +# Outputs: [ 2 ; 3 ; 4 ] +``` + + +```cameligo group=c +let incr_map (l : int list) : int list = + List.map (fun (i : int) -> i + 1) l +``` +You can call the function `incr_map` defined above using the LIGO compiler +like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/functions/incr_map.mligo incr_map +"list [1;2;3]" +# Outputs: [ 2 ; 3 ; 4 ] +``` + + +```reasonligo group=c +let incr_map = (l : list (int)) : list (int) => + List.map ((i : int) => i + 1, l); +``` +You can call the function `incr_map` defined above using the LIGO compiler +like so: +```shell +ligo run-function +gitlab-pages/docs/language-basics/src/functions/incr_map.religo incr_map +"list [1;2;3]" +# Outputs: [ 2 ; 3 ; 4 ] +``` + + diff --git a/gitlab-pages/docs/language-basics/src/functions/incr_map.ligo b/gitlab-pages/docs/language-basics/src/functions/incr_map.ligo new file mode 100644 index 000000000..e5f915847 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/functions/incr_map.ligo @@ -0,0 +1,5 @@ +function increment (const b : int) : int is + (function (const a : int) : int is a + 1) (b) + +function incr_map (const l : list (int)) : list (int) is + list_map (function (const i : int) : int is i + 1, l) diff --git a/gitlab-pages/docs/language-basics/src/functions/incr_map.mligo b/gitlab-pages/docs/language-basics/src/functions/incr_map.mligo new file mode 100644 index 000000000..b2dd363c8 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/functions/incr_map.mligo @@ -0,0 +1,4 @@ +let increment (b : int) : int = (fun (a : int) -> a + 1) b + +let incr_map (l : int list) : int list = + List.map (fun (i : int) -> i + 1) l diff --git a/gitlab-pages/docs/language-basics/src/functions/incr_map.religo b/gitlab-pages/docs/language-basics/src/functions/incr_map.religo new file mode 100644 index 000000000..85b8a39f4 --- /dev/null +++ b/gitlab-pages/docs/language-basics/src/functions/incr_map.religo @@ -0,0 +1,4 @@ +let increment = (b : int) : int => ((a : int) : int => a + 1)(b); + +let incr_map = (l : list (int)) : list (int) => + List.map ((i : int) => i + 1, l); From c82ae6359a96e88cf0bf8fea7309bf53656c43c8 Mon Sep 17 00:00:00 2001 From: Lesenechal Remi Date: Thu, 6 Feb 2020 13:32:51 +0100 Subject: [PATCH 8/9] fix broken .md file (sets-lists-tuples) --- .../docs/language-basics/sets-lists-tuples.md | 111 ++++++++---------- 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/gitlab-pages/docs/language-basics/sets-lists-tuples.md b/gitlab-pages/docs/language-basics/sets-lists-tuples.md index 330187ac3..e43de8200 100644 --- a/gitlab-pages/docs/language-basics/sets-lists-tuples.md +++ b/gitlab-pages/docs/language-basics/sets-lists-tuples.md @@ -128,43 +128,39 @@ let my_list : list (int) = [1, 2, 2]; // The head is 1 ### Adding to a List - Lists can be augmented by adding an element before the head (or, in terms of stack, by *pushing an element on top*). This operation is usually called *consing* in functional languages. - - In PascaLIGO, the *cons operator* is infix and noted `#`. It is not symmetric: on the left lies the element to cons, and, on the right, a list on which to cons. (The symbol is helpfully asymmetric to remind you of that.) -```pascaligo group=b -const larger_list : list (int) = 5 # my_list -``` - - - In CameLIGO, the *cons operator* is infix and noted `::`. It is not symmetric: on the left lies the element to cons, and, on the right, a list on which to cons. -```cameligo group=b -let larger_list : int list = 5 :: my_list -``` - - - In ReasonLIGO, the *cons operator* is infix and noted `, ...`. It is not symmetric: on the left lies the element to cons, and, on the right, a list on which to cons. + + +```pascaligo group=b +const larger_list : list (int) = 5 # my_list +``` + + +```cameligo group=b +let larger_list : int list = 5 :: my_list +``` + + ```reasonligo group=b let larger_list : list (int) = [5, ...my_list]; ``` -
@@ -187,41 +183,32 @@ functions in LIGO. They take as a parameter the function to apply to all the elements. Of course, that function must return a value of the same type as the element. - - - - In PascaLIGO, the map function is called `list_map`. +In CameLIGO and ReasonLIGO, the map function is called `List.map`. + + + ```pascaligo group=b function increment (const i : int): int is i + 1 // Creates a new list with all elements incremented by 1 const plus_one : list (int) = list_map (increment, larger_list) ``` - - -In CameLIGO, the map function is called `List.map`. - ```cameligo group=b let increment (i : int) : int = i + 1 // Creates a new list with all elements incremented by 1 let plus_one : int list = List.map increment larger_list ``` - - -In CameLIGO, the map function is called `List.map`. - ```reasonligo group=b let increment = (i : int) : int => i + 1; // Creates a new list with all elements incremented by 1 let plus_one : list (int) = List.map (increment, larger_list); ``` - @@ -278,16 +265,17 @@ let my_set : set (int) = (Set.empty : set (int)); ### Non-empty Sets - - In PascaLIGO, the notation for sets is similar to that for lists, except the keyword `set` is used before: + + ```pascaligo group=a const my_set : set (int) = set [3; 2; 2; 1] ``` + You can check that `2` is not repeated in `my_set` by using the LIGO compiler like this (the output will sort the elements of the set, but @@ -299,16 +287,17 @@ gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo my_set # Outputs: { 3 ; 2 ; 1 } ``` - - In CameLIGO, there is no predefined syntactic construct for sets: you must build your set by adding to the empty set. (This is the way in OCaml.) + + ```cameligo group=a let my_set : int set = Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set)))) ``` + You can check that `2` is not repeated in `my_set` by using the LIGO compiler like this (the output will sort the elements of the set, but @@ -320,16 +309,17 @@ gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo my_set # Outputs: { 3 ; 2 ; 1 } ``` - - In ReasonLIGO, there is no predefined syntactic construct for sets: you must build your set by adding to the empty set. (This is the way in OCaml.) + + ```reasonligo group=a let my_set : set (int) = Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int))))); ``` + You can check that `2` is not repeated in `my_set` by using the LIGO compiler like this (the output will sort the elements of the set, but @@ -341,71 +331,63 @@ gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo my_set # Outputs: { 3 ; 2 ; 1 } ``` - - ### Set Membership - - PascaLIGO features a special keyword `constains` that operates like an infix operator checking membership in a set. + + ```pascaligo group=a const contains_3 : bool = my_set contains 3 ``` - ```cameligo group=a let contains_3 : bool = Set.mem 3 my_set ``` - ```reasonligo group=a let contains_3 : bool = Set.mem (3, my_set); ``` - ### Cardinal - ```pascaligo group=a const set_size : nat = size (my_set) ``` - ```cameligo group=a let set_size : nat = Set.size my_set ``` - ```reasonligo group=a let set_size : nat = Set.size (my_set); ``` - ### Adding or Removing from a Set - - In PascaLIGO, there are two ways to update a set. Either we create a new set from the given one, or we modify it in-place. First, let us consider the former: + + ```pascaligo group=a const larger_set : set (int) = set_add (4, my_set) const smaller_set : set (int) = set_remove (3, my_set) ``` + If we are in a block, we can use an instruction to modify the set bound to a given variable. This is called a *patch*. It is only @@ -418,61 +400,69 @@ this instruction is equivalent to perform the union of two sets, one that is modified in-place, and the other given as a literal (extensional definition). -``pascaligo group=a + + +```pascaligo group=a function update (var s : set (int)) : set (int) is block { patch s with set [4; 7] } with s const new_set : set (int) = update (my_set) ``` + - In CameLIGO, we update a given set by creating another one, with or without some elements. + + ```cameligo group=a let larger_set : int set = Set.add 4 my_set let smaller_set : int set = Set.remove 3 my_set ``` + - In ReasonLIGO, we update a given set by creating another one, with or without some elements. + + ```reasonligo group=a let larger_set : set (int) = Set.add (4, my_set); let smaller_set : set (int) = Set.remove (3, my_set); ``` - ### Folding over a Set - Given a set, we may want to apply a function in turn to all the elements it contains, while accumulating some value which is returned at the end. This is a *fold operation*. In the following example, we sum up all the elements of the set `my_set` defined above. - In PascaLIGO, the folded function takes the accumulator first and the (current) set element second. The predefined fold is called `set_fold`. + + ```pascaligo group=a function sum (const acc : int; const i : int): int is acc + i const sum_of_elements : int = set_fold (sum, my_set, 0) ``` + It is possible to use a *loop* over a set as well. + + ```pascaligo group=a function loop (const s : set (int)) : int is block { var sum : int := 0; @@ -481,26 +471,27 @@ function loop (const s : set (int)) : int is block { } } with sum ``` - - + In CameLIGO, the predefined fold over sets is called `Set.fold`. + + ```cameligo group=a let sum (acc, i : int * int) : int = acc + i let sum_of_elements : int = Set.fold sum my_set 0 ``` + - -` In ReasonLIGO, the predefined fold over sets is called `Set.fold`. -``reasonligo group=a + + +```reasonligo group=a let sum = ((acc, i) : (int, int)) : int => acc + i; let sum_of_elements : int = Set.fold (sum, my_set, 0); ``` - From 82aacde97fcffc8ea57157b96142ecfe388497e1 Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Mon, 10 Feb 2020 19:07:20 +0100 Subject: [PATCH 9/9] More documentation rewrites. --- .../docs/advanced/entrypoints-contracts.md | 61 +- gitlab-pages/docs/advanced/first-contract.md | 48 +- .../docs/advanced/timestamps-addresses.md | 14 +- .../intro/src/what-and-why/ligo-counter.ligo | 35 +- .../intro/src/what-and-why/ligo-counter.mligo | 23 + .../src/what-and-why/ligo-counter.religo | 26 + gitlab-pages/docs/intro/what-and-why.md | 252 ++++++--- .../docs/language-basics/boolean-if-else.md | 17 +- .../docs/language-basics/functions.md | 93 ++-- gitlab-pages/docs/language-basics/loops.md | 76 +-- .../docs/language-basics/maps-records.md | 289 +++++++--- .../docs/language-basics/math-numbers-tez.md | 22 +- .../docs/language-basics/sets-lists-tuples.md | 526 +++++++++++++----- gitlab-pages/docs/language-basics/strings.md | 24 +- .../docs/language-basics/tezos-specific.md | 19 +- gitlab-pages/docs/language-basics/types.md | 114 +++- .../unit-option-pattern-matching.md | 26 +- .../variables-and-constants.md | 27 +- gitlab-pages/website/core/CodeExamples.js | 93 ++-- gitlab-pages/website/pages/en/index.js | 8 +- gitlab-pages/website/siteConfig.js | 13 +- .../version-next/contributors/origin.md | 4 +- .../version-next/contributors/philosophy.md | 11 +- .../version-next-sidebars.json | 22 +- src/passes/operators/operators.ml | 5 +- 25 files changed, 1238 insertions(+), 610 deletions(-) create mode 100644 gitlab-pages/docs/intro/src/what-and-why/ligo-counter.mligo create mode 100644 gitlab-pages/docs/intro/src/what-and-why/ligo-counter.religo diff --git a/gitlab-pages/docs/advanced/entrypoints-contracts.md b/gitlab-pages/docs/advanced/entrypoints-contracts.md index 29160031c..07bb7ee0c 100644 --- a/gitlab-pages/docs/advanced/entrypoints-contracts.md +++ b/gitlab-pages/docs/advanced/entrypoints-contracts.md @@ -1,20 +1,20 @@ --- id: entrypoints-contracts -title: Entrypoints to Contracts +title: Access function and Entrypoints --- -## Entrypoints +## Access Functions A LIGO contract is made of a series of constant and function declarations. Only functions having a special type can be called when -the contract is activated: they are called *entrypoints*. An -entrypoint need to take two parameters, the *contract parameter* and -the *on-chain storage*, and return a pair made of a *list of -operations* and a (new) storage. +the contract is activated: we called them *access functions*. An +access function takes two parameters, the *contract parameter* and the +*on-chain storage*, and returns a pair made of a *list of operations* +and a (new) storage. When the contract is originated, the initial value of the storage is -provided. When and entrypoint is later called, only the parameter is -provided, but the type of an entrypoint contains both. +provided. When an access function is later called, only the parameter +is provided, but the type of an access function contains both. The type of the contract parameter and the storage are up to the contract designer, but the type for list operations is not. The return @@ -23,7 +23,7 @@ has been defined elsewhere. (Note that you can use any type with any name for the storage.) - + ```pascaligo skip type storage is ... // Any name, any type type return is list (operation) * storage @@ -42,9 +42,9 @@ type return = (list (operation), storage); ``` -The contract storage can only be modified by activating an -entrypoint. It is important to understand what that means. What it -does *not* mean is that some global variable holding the storage is +The contract storage can only be modified by activating an access +function. It is important to understand what that means. What it does +*not* mean is that some global variable holding the storage is modified by the entrypoint. Instead, what it *does* mean is that, given the state of the storage *on-chain*, an entrypoint specifies how to create another state for it, depending on a parameter. @@ -54,7 +54,7 @@ is updated by the parameter. - + ```pascaligo group=a type storage is nat @@ -82,20 +82,29 @@ let main = ((parameter, store): (nat, storage)) : return => { ``` -In LIGO, the design pattern for entrypoints consists in actually -having exactly *one entrypoint*, like the `main` function in C. The -parameter of the contract is then a variant type, and, depending on -the constructors of that type, different functions in the contract are -called. In other terms, the unique entrypoint dispatches the control -flow depending on a *pattern matching* on the contract parameter. +## Entrypoints -In the following example, the storage contains a counter (of type -`nat`) and a name (of type `string`). Depending on the parameter of -the contract, either the counter or the name is updated. +In LIGO, the design pattern is to have *one* access function that +dispatches the control flow according to its parameter. Those +functions called for those actions are called *entrypoints*. + +As an analogy, in the C programming language, the `main` function is +the unique access function and any function called from it would be an +entrypoint. + +The parameter of the contract is then a variant type, and, depending +on the constructors of that type, different functions in the contract +are called. In other terms, the unique access function dispatches the +control flow depending on a *pattern matching* on the contract +parameter. + +In the following example, the storage contains a counter of type `nat` +and a name of type `string`. Depending on the parameter of the +contract, either the counter or the name is updated. - + ```pascaligo group=b type parameter is Entrypoint_A of nat @@ -188,7 +197,7 @@ any transaction that sends more tez than `0mutez`, that is, no incoming tokens are accepted. - + ```pascaligo group=c type parameter is unit type storage is unit @@ -232,7 +241,7 @@ let deny = ((param, store): (parameter, storage)) : return => { This example shows how `sender` or `source` can be used to deny access to an entrypoint. - + ```pascaligo group=c const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); @@ -287,7 +296,7 @@ counter contract. - + ```pascaligo skip // counter.ligo type parameter is diff --git a/gitlab-pages/docs/advanced/first-contract.md b/gitlab-pages/docs/advanced/first-contract.md index 19861291f..1e9c5f120 100644 --- a/gitlab-pages/docs/advanced/first-contract.md +++ b/gitlab-pages/docs/advanced/first-contract.md @@ -3,20 +3,24 @@ id: first-contract title: First contract --- -So far so good, we've learned enough of the LIGO language, we're confident enough to write out first smart contract. +So far so good, we have learned enough of the LIGO language, we are +confident enough to write out first smart contract. -We'll be implementing a counter contract, let's go. +We will be implementing a counter contract. -## Dry-running a contract +## Dry-running a Contract -Testing a contract can be quite easy if we utilize LIGO's built-in dry run feature. Dry-run works by simulating the entrypoint execution, as if it were deployed on a real chain. You need to provide the following: +Testing a contract can be quite easy if we utilize LIGO's built-in dry +run feature. Dry-run works by simulating the access function +execution, as if it were deployed on a real chain. You need to provide +the following: - `file` - contract to run - `entrypoint` - name of the function to execute -- `parameter` - parameter passed to the entrypoint (in a theoretical invocation operation) +- `parameter` - parameter passed to the access function (in a theoretical invocation operation) - `storage` - a mock storage value, as if it were stored on a real chain -Here's a full example: +Here is a full example: @@ -29,25 +33,29 @@ ligo dry-run src/basic.ligo main Unit Unit ``` -Output of the `dry-run` is the return value of our entrypoint function, we can see the operations emited - in our case an empty list, and the new storage value being returned - which in our case is still `Unit`. +Output of the `dry-run` is the return value of our access function, we +can see the operations emited - in our case an empty list, and the new +storage value being returned - which in our case is still `Unit`. -## Building a counter contract +## A Counter Contract -Our counter contract will store a single `int` as it's storage, and will accept an `action` variant in order to re-route our single `main` entrypoint into two entrypoints for `addition` and `subtraction`. +Our counter contract will store a single `int` as it's storage, and +will accept an `action` variant in order to re-route our single `main` +access function to two entrypoints for `addition` and `subtraction`. ``` type action is -| Increment of int + Increment of int | Decrement of int function main (const p : action ; const s : int) : (list(operation) * int) is - block {skip} with ((nil : list(operation)), - case p of + ((nil : list(operation)), + (case p of | Increment (n) -> s + n | Decrement (n) -> s - n - end) + end)) ``` @@ -98,11 +106,13 @@ ligo dry-run src/counter.ligo main "Increment(5)" 5 -Yay, our contract's storage has been successfuly incremented to `10`. +Our contract's storage has been successfuly incremented to `10`. ## Deploying and interacting with a contract on a live-chain -In order to deploy the counter contract to a real Tezos network, we'd have to compile it first, this can be done with the help of the `compile-contract` CLI command: +In order to deploy the counter contract to a real Tezos network, we'd +have to compile it first, this can be done with the help of the +`compile-contract` CLI command: @@ -156,7 +166,10 @@ Command above will output the following Michelson code: ``` -However in order to originate a Michelson contract on Tezos, we also need to provide the initial storage value, we can use `compile-storage` to compile the LIGO representation of the storage to Michelson. +However in order to originate a Michelson contract on Tezos, we also +need to provide the initial storage value, we can use +`compile-storage` to compile the LIGO representation of the storage to +Michelson. @@ -182,4 +195,5 @@ ligo compile-parameter src/counter.ligo main 'Increment(5)' -Now we can use `(Right 5)` which is a Michelson value, to invoke our contract - e.g. via `tezos-client` +Now we can use `(Right 5)` which is a Michelson value, to invoke our +contract - e.g. via `tezos-client` diff --git a/gitlab-pages/docs/advanced/timestamps-addresses.md b/gitlab-pages/docs/advanced/timestamps-addresses.md index 8c20da011..5174e1cc8 100644 --- a/gitlab-pages/docs/advanced/timestamps-addresses.md +++ b/gitlab-pages/docs/advanced/timestamps-addresses.md @@ -16,7 +16,7 @@ expression, please be aware that it is up to the baker to set the current timestamp value. - + ```pascaligo group=a const today : timestamp = now ``` @@ -44,7 +44,7 @@ constraints on your smart contracts. Consider the following scenarios. #### In 24 hours - + ```pascaligo group=b const today : timestamp = now const one_day : int = 86400 @@ -76,7 +76,7 @@ let one_day_later : timestamp = some_date + one_day; #### 24 hours Ago - + ```pascaligo group=c const today : timestamp = now const one_day : int = 86400 @@ -105,7 +105,7 @@ You can compare timestamps using the same comparison operators applying to numbers. - + ```pascaligo group=c const not_tommorow : bool = (now = in_24_hrs) ``` @@ -130,7 +130,7 @@ type `address`. Beware of failures if the address is invalid. Consider the following examples. - + ```pascaligo group=d const my_account : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) @@ -159,7 +159,7 @@ failure if the signature is invalid. Here is how you can define a signature: - + ```pascaligo group=e const my_sig : signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" : @@ -188,7 +188,7 @@ failure if the key is invalid. Here is how you can define a key. - + ```pascaligo group=f const my_key : key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key) diff --git a/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.ligo b/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.ligo index 989afba07..4fd886fb7 100644 --- a/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.ligo +++ b/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.ligo @@ -1,12 +1,25 @@ -type action is -| Increment of int -| Decrement of int -| Reset of unit +type storage is int -function main (const p : action ; const s : int) : (list(operation) * int) is - block { skip } with ((nil : list(operation)), - case p of - | Increment(n) -> s + n - | Decrement(n) -> s - n - | Reset(n) -> 0 - end) \ No newline at end of file +type parameter is + Increment of int +| Decrement of int +| Reset + +type return is list (operation) * storage + +(* Two entrypoints *) + +function add (const store : storage; const delta : int) : storage is store + delta + +function sub (const store : storage; const delta : int) : storage is store - delta + +(* Main access point that dispatches to the entrypoints according to + the smart contract parameter. *) + +function main (const action : parameter; const store : storage) : return is + ((nil : list (operation)), // No operations + case action of + Increment (n) -> add (store, n) + | Decrement (n) -> sub (store, n) + | Reset -> 0 + end) diff --git a/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.mligo b/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.mligo new file mode 100644 index 000000000..3cfa8551f --- /dev/null +++ b/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.mligo @@ -0,0 +1,23 @@ +type storage = int + +type parameter = + Increment of int +| Decrement of int +| Reset + +type return = operation list * storage + +(* Two entrypoints *) + +let add (store, delta : storage * int) : storage = store + delta +let sub (store, delta : storage * int) : storage = store - delta + +(* Main access point that dispatches to the entrypoints according to + the smart contract parameter. *) + +let main (action, store : parameter * storage) : return = + ([] : operation list), // No operations + (match action with + Increment (n) -> add (store, n) + | Decrement (n) -> sub (store, n) + | Reset -> 0) diff --git a/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.religo b/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.religo new file mode 100644 index 000000000..74292563c --- /dev/null +++ b/gitlab-pages/docs/intro/src/what-and-why/ligo-counter.religo @@ -0,0 +1,26 @@ +type storage = int; + +type parameter = + Increment (int) +| Decrement (int) +| Reset; + +type return = (list (operation), storage); + +(* Two entrypoints *) + +let add = ((store, delta) : (storage, int)) : storage => store + delta; + +let sub = ((store, delta) : (storage, int)) : storage => store - delta; + +(* Main access point that dispatches to the entrypoints according to + the smart contract parameter. *) + +let main = ((action, store) : (parameter, storage)) : return => { + (([] : list (operation)), // No operations + (switch (action) { + | Increment (n) => add ((store, n)) + | Decrement (n) => sub ((store, n)) + | Reset => 0 + })) +}; diff --git a/gitlab-pages/docs/intro/what-and-why.md b/gitlab-pages/docs/intro/what-and-why.md index fdd1d2887..f9e627efd 100644 --- a/gitlab-pages/docs/intro/what-and-why.md +++ b/gitlab-pages/docs/intro/what-and-why.md @@ -1,14 +1,73 @@ --- id: what-and-why -title: What & Why +title: Michelson and LIGO --- -Before we get into what LIGO is and why LIGO needs to exist, let's take a look at what options the Tezos blockchain offers us out of the box. If you want to implement smart contracts natively on Tezos, you have to learn [Michelson](https://tezos.gitlab.io/whitedoc/michelson.html). +Before we get into what LIGO is and why LIGO needs to exist, let us +take a look at what options the Tezos blockchain offers us out of the +box. If you want to implement smart contracts natively on Tezos, you +have to learn +[Michelson](https://tezos.gitlab.io/whitedoc/michelson.html). -> 💡 The (Michelson) language is stack-based, with high level data types and primitives and strict static type checking. +**The rationale and design of Michelson** +The language native to the Tezos blockchain for writing smart +contracts is *Michelson*, a Domain-Specific Language (DSL) inspired by +Lisp and Forth. This unusual lineage aims at satisfying unusual +constraints, but entails some tensions in the design. -Here's an example of Michelson code: +First, to measure stepwise gas consumption, *Michelson is interpreted*. + +On the one hand, to assess gas usage per instruction, instructions +should be simple, which points to low-level features (a RISC-like +language). On the other hand, it was originally thought that users +will want to write in Michelson instead of lowering a language to +Michelson, because the gas cost would otherwise be harder to +predict. This means that *high-level features* were deemed necessary +(like a restricted variant of Lisp lambdas, a way to encode algebraic +data types, as well as built-in sets, maps and lists). + +To avoid ambiguous and otherwise misleading contracts, the layout of +Michelson contracts has been constrained (e.g., indentation, no +UTF-8), and a *canonical form* was designed and enforced when storing +contracts on the chain. + +To reduce the size of the code, Michelson was designed as *a +stack-based language*, whence the lineage from Forth and other +concatenative languages like PostScript, Joy, Cat, Factor etc. (Java +bytecode would count too.) + +Programs in those languages are *compact* because they assume an +implicit stack in which some input values are popped, and output +values are pushed, according to the current instruction being +executed. + +*Each Michelson instruction modifies a prefix of the stack*, that is, +a segment starting at the top. + +Whilst the types of Michelson instructions can be polymorphic, their +instantiations must be monomorphic, hence *Michelson instructions are +not first-class values* and cannot be partially interpreted. + +This enables a simple *static type checking*, as opposed to a complex +type inference. It can be performed efficiently: *contract type +checking consumes gas*. Basically, type checking aims at validating +the composition of instructions, therefore is key to safely composing +contracts (concatenation, activations). Once a contract passes type +checking, it cannot fail due to inconsistent assumptions on the +storage and other values (there are no null values, no casts), but it +can still fail for other reasons: division by zero, token exhaustion, +gas exhaustion, or an explicit `FAILWITH` instruction. This property +is called *type safety*. Also, such a contract cannot remain stuck: +this is the *progress property*. + +The existence of a formal type system for Michelson, of a formal +specification of its dynamic semantics (evaluation), of a Michelson +interpreter in Coq, of proofs in Coq of properties of some typical +contracts, all those achievements are instances of *formal methods in +Tezos*. + +Here is an example of a Michelson contract. **`counter.tz`** ```text @@ -21,118 +80,167 @@ Here's an example of Michelson code: NIL operation ; PAIR } } ``` -The contract above maintains an `int` in its storage. It has two entrypoints *(functions)* `add` and `sub` to modify it, and the default *entrypoint* of type unit will reset it to 0. +The contract above maintains an `int` as its storage. It has two +[entrypoints](https://tezos.gitlab.io/whitedoc/michelson.html#entrypoints), +`add` and `sub`, to modify it, and the `default` entrypoint of type +`unit` will reset it to `0`. -The contract itself contains three main parts: +The contract itself contains three sections: +- `parameter` - The argument provided by a transaction invoking the contract. +- `storage` - The type definition for the contract's data storage. +- `code` - Actual Michelson code that has the provided parameter and + the current storage value in its initial stack. It outputs in the + resulting stack a pair made of a list of operations and a new + storage value. -- `parameter` - Argument provided by a transaction invoking the contract -- `storage` - Type definition for the contract's data storage. -- `code` - Actual Michelson code that has the provided parameter & the current storage value in its initial stack. It outputs a pair of operations and a new storage value as its resulting stack. +Michelson code consists of *instructions* like `IF_LEFT`, `PUSH ...`, +`UNPAIR` etc. that are composed sequentially in what is called a +*sequence*. The implicit stack contains at all times the state of the +evaluation of the program, whilst the storage represents the +persistent state. If the contract execution is successful, the new +storage state will be committed to the chain and become visible to all +the nodes. Instructions are used to transform a prefix of the stack, +that is, the topmost part of it, for example, by duplicating its top +element, dropping it, subtracting the first two etc. -Michelson code consists of *instructions* like `IF_LEFT`, `PUSH ...`, `UNPAIR` that are bundled togeter in what is called a *sequence*. Stack represents an intermediate state of the program, while **storage represents a persistent state**. Instructions are used to modify the run-time stack in order to yield a desired stack value when the program terminates. +> 💡 A Michelson program running on the Tezos blockchain is meant to +> output a pair of values including a `list of operations` to include +> in a transaction, and a new `storage` value to persist on the chain. -> 💡 A Michelson program running on the Tezos blockchain is meant to output a pair of values including a `list of operations` to emit and a new `storage` value to persist +## Stack versus variables -## Differences between a stack and traditional variable management - -Stack management might be a little bit challanging, especially if you're coming from a *C-like language*. Let's implement a similar program in Javascript: +Perhaps the biggest challenge when programming in Michelson is the +lack of *variables* to denote the data: the stack layout has to be +kept in mind when retrieving and storing data. For example, let us +implement a program in Javascript that is similar to the Michelson +above: **`counter.js`** ```javascript var storage = 0; -function add(a) { - storage += a -} +function add (a) { storage += a; } +function sub (a) { storage -= a; } -function sub(a) { - storage -= a -} +// We are calling this function "reset" instead of "default" +// because `default` is a Javascript keyword -// We're calling this function reset instead of default -// because `default` is a javascript keyword -function reset() { - storage = 0; -} +function reset () { storage = 0; } ``` -In our javascript program the initial `storage` value is `0` and it can be modified by running the functions `add(a)`, `sub(a)` and `reset()`. +In our Javascript program the initial `storage` value is `0` and it +can be modified by calling `add (a)`, `sub (a)` and `reset ()`. -Unfortunately (???), we **can't run Javascript on the Tezos blockchain** at the moment. But we can choose LIGO, which will abstract the stack management and allow us to create readable, type-safe, and efficient smart contracts. +We cannot run Javascript on the Tezos blockchain, but we can choose +LIGO, which will abstract the stack management and allow us to create +readable, type-safe, and efficient smart contracts. -> 💡 You can try running the javascript program [here](https://codepen.io/maht0rz/pen/dyyvoPQ?editors=0012) +## LIGO for Programming Smart Contracts on Tezos -## C-like smart contracts instead of Michelson +Perhaps the most striking feature of LIGO is that it comes in +different concrete syntaxes, and even different programming +paradigms. In other words, LIGO is not defined by one syntax and one +paradigm, like imperative versus functional. -Let's take a look at a similar LIGO program. Don't worry if it's a little confusing at first; we'll explain all the syntax in the upcoming sections of the documentation. + - There is **PascaLIGO**, which is inspired by Pascal, hence is an + imperative language with lots of keywords, where values can be + locally mutated after they have been annotated with their types + (declaration). + + - There is **CameLIGO**, which is inspired by the pure subset of + [OCaml](https://ocaml.org/), hence is a functional language with + few keywords, where values cannot be mutated, but still require + type annotations (unlike OCaml, whose compiler performs almost + full type inference). + + - There is **ReasonLIGO**, which is inspired by the pure subset of + [ReasonML](https://reasonml.github.io/), which is based upon + OCaml. + +Let us decline the same LIGO contract in the three flavours above. Do +not worry if it is a little confusing at first; we will explain all +the syntax in the upcoming sections of the documentation. -```pascaligo -type action is -| Increment of int -| Decrement of int -| Reset of unit +```pascaligo group=a +type storage is int -function main (const p : action ; const s : int) : (list(operation) * int) is - block { skip } with ((nil : list(operation)), - case p of - | Increment(n) -> s + n - | Decrement(n) -> s - n - | Reset(n) -> 0 +type parameter is + Increment of int +| Decrement of int +| Reset + +type return is list (operation) * storage + +function main (const action : parameter; const store : storage) : return is + ((nil : list (operation)), + case action of + Increment (n) -> store + n + | Decrement (n) -> store - n + | Reset -> 0 end) ``` -```cameligo -type action = -| Increment of int -| Decrement of int -| Reset of unit +```cameligo group=a +type storage = int -let main (p, s: action * int) : operation list * int = - let result = - match p with - | Increment n -> s + n - | Decrement n -> s - n - | Reset n -> 0 - in - (([]: operation list), result) +type parameter = + Increment of int +| Decrement of int +| Reset + +type return = operation list * storage + +let main (action, store : parameter * storage) : return = + ([] : operation list), + (match action with + Increment n -> store + n + | Decrement n -> store - n + | Reset -> 0) ``` -```reasonligo -type action = -| Increment(int) -| Decrement(int) -| Reset(unit); +```reasonligo group=a +type storage = int; -let main = ((p,s): (action, int)) : (list(operation), int) => { - let result = - switch (p) { - | Increment(n) => s + n - | Decrement(n) => s - n - | Reset n => 0 - }; - (([]: list(operation)), result); +type parameter = + Increment (int) +| Decrement (int) +| Reset; + +type return = (list (operation), storage); + +let main = ((action, store): (parameter, storage)) : return => { + (([] : list (operation)), + (switch (action) { + | Increment (n) => store + n + | Decrement (n) => store - n + | Reset => 0})); }; ``` + + -> 💡 You can find the Michelson compilation output of the contract above in **`ligo-counter.tz`** +This LIGO contract behaves almost exactly* like the Michelson +contract we saw first, and it accepts the following LIGO expressions: +`Increment(n)`, `Decrement(n)` and `Reset`. Those serve as +`entrypoint` identification, same as `%add` `%sub` or `%default` in +the Michelson contract. -The LIGO contract behaves exactly* like the Michelson contract we've saw first, and it accepts the following LIGO expressions/values: `Increment(n)`, `Decrement(n)` and `Reset(n)`. Those serve as `entrypoint` identification, same as `%add` `%sub` or `%default` in the Michelson contract. - -**not exactly, the Michelson contract also checks if the `AMOUNT` sent is `0`* +**The Michelson contract also checks if the `AMOUNT` sent is `0`* --- ## Runnable code snippets & exercises Some of the sections in this documentation will include runnable code snippets and exercises. Sources for those are available at -the [LIGO Gitlab repository](https://gitlab.com/ligolang/ligo). +the [LIGO Gitlab repository](https://gitlab.com/ligolang/ligo). ### Snippets For example **code snippets** for the *Types* subsection of this doc, can be found here: diff --git a/gitlab-pages/docs/language-basics/boolean-if-else.md b/gitlab-pages/docs/language-basics/boolean-if-else.md index d938f5b7b..56d565cec 100644 --- a/gitlab-pages/docs/language-basics/boolean-if-else.md +++ b/gitlab-pages/docs/language-basics/boolean-if-else.md @@ -9,7 +9,7 @@ The type of a boolean value is `bool`. Here is how to define a boolean value: - + ```pascaligo group=a const a : bool = True // Notice the capital letter const b : bool = False // Same. @@ -19,7 +19,6 @@ const b : bool = False // Same. let a : bool = true let b : bool = false ``` - ```reasonligo group=a let a : bool = true; @@ -27,8 +26,7 @@ let b : bool = false; ``` - -## Comparing two Values +## Comparing Values In LIGO, only values of the same type can be compared. Moreover, not all values of the same type can be compared, only those with @@ -42,7 +40,7 @@ function. ### Comparing Strings - + ```pascaligo group=b const a : string = "Alice" const b : string = "Alice" @@ -62,11 +60,10 @@ let c : bool = (a == b); // true ``` - ### Comparing numbers - + ```pascaligo group=c const a : int = 5 const b : int = 4 @@ -102,14 +99,13 @@ let h : bool = (a != b); ``` - ### Comparing tez > 💡 Comparing `tez` values is especially useful when dealing with an > amount sent in a transaction. - + ```pascaligo group=d const a : tez = 5mutez const b : tez = 10mutez @@ -136,7 +132,7 @@ Conditional logic enables forking the control flow depending on the state. - + ```pascaligo group=e type magnitude is Small | Large // See variant types. @@ -183,5 +179,4 @@ ligo run-function gitlab-pages/docs/language-basics/boolean-if-else/cond.religo compare 21n' # Outputs: Large ``` - diff --git a/gitlab-pages/docs/language-basics/functions.md b/gitlab-pages/docs/language-basics/functions.md index e68c93ed8..9fe1ff4f2 100644 --- a/gitlab-pages/docs/language-basics/functions.md +++ b/gitlab-pages/docs/language-basics/functions.md @@ -3,47 +3,42 @@ id: functions title: Functions --- -Writing code is fun as long as it does not get out of hand. To make -sure our code does not turn into spaghetti, we can structure some -logic into functions. +LIGO features functions are the basic building block of contracts. For +example, entrypoints are functions. -## Blocks +## Declaring Functions + + + + +There are two ways in PascaLIGO to define functions: with or without a +*block*. + +### Blocks In PascaLIGO, *blocks* enable the sequential composition of instructions into an isolated scope. Each block needs to include at -least one instruction. If we need a placeholder, we use the -instruction `skip` which leaves the state unchanged. The rationale -for `skip` instead of a truly empty block is that it prevents you from -writing an empty block by mistake. - - - +least one instruction. ```pascaligo skip -// terse style block { a := a + 1 } -// verbose style -begin - a := a + 1 -end ``` -Blocks are more versatile than simply containing instructions: -they can also include *declarations* of values, like so: + +If we need a placeholder, we use the instruction `skip` which leaves +the state unchanged. The rationale for `skip` instead of a truly +empty block is that it prevents you from writing an empty block by +mistake. + ```pascaligo skip -// terse style -block { const a : int = 1 } -// verbose style -begin - const a : int = 1 -end +block { skip } ``` - +Blocks are more versatile than simply containing instructions: they +can also include *declarations* of values, like so: -## Defining a function - - - +```pascaligo skip +block { const a : int = 1 } +``` Functions in PascaLIGO are defined using the `function` keyword followed by their `name`, `parameters` and `return` type definitions. @@ -60,15 +55,23 @@ function add (const a : int; const b : int) : int is The function body consists of two parts: -- `block { }` - logic of the function -- `with ` - the value returned by the function +- `block { }` is the logic of the function; +- `with ` is the value returned by the function. -#### Blockless functions +### Blockless functions + +Functions that can contain all of their logic into a single +*expression* can be defined without the need of a block: + +```pascaligo +function identity (const n : int) : int is block { skip } with n // Bad! Empty block not needed! + +function identity (const n : int) : int is n // Blockless +``` + +The value of the expression is implicitly returned by the +function. Another example is as follows: -Functions that can contain all of their logic into a single expression -can be defined without a block. Instead of a block, you put an -expression, whose value is implicitly returned by the function, like -so: ```pascaligo group=b function add (const a: int; const b : int) : int is a + b ``` @@ -134,10 +137,10 @@ ligo run-function gitlab-pages/docs/language-basics/src/functions/curry.mligo in The function body is a single expression, whose value is returned. - -Functions in ReasonLIGO are defined using the `let` keyword, like -other values. The difference is that a succession of parameters is -provided after the value name, followed by the return type. + Functions in ReasonLIGO are defined using the `let` +keyword, like other values. The difference is that a tuple of +parameters is provided after the value name, with its type, then +followed by the return type. Here is how you define a basic function that sums two integers: ```reasonligo group=b @@ -163,7 +166,7 @@ a key in a record or a map. Here is how to define an anonymous function: - + ```pascaligo group=c function increment (const b : int) : int is (function (const a : int) : int is a + 1) (b) @@ -194,7 +197,7 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.mligo a ```reasonligo group=c let increment = (b : int) : int => ((a : int) : int => a + 1)(b); -let a : int = increment (1); // a = 2 +let a : int = increment (1); // a == 2 ``` You can check the value of `a` defined above using the LIGO compiler @@ -212,7 +215,7 @@ the use case of having a list of integers and mapping the increment function to all its elements. - + ```pascaligo group=c function incr_map (const l : list (int)) : list (int) is list_map (function (const i : int) : int is i + 1, l) @@ -226,7 +229,7 @@ gitlab-pages/docs/language-basics/src/functions/incr_map.ligo incr_map # Outputs: [ 2 ; 3 ; 4 ] ``` - + ```cameligo group=c let incr_map (l : int list) : int list = List.map (fun (i : int) -> i + 1) l @@ -240,7 +243,7 @@ gitlab-pages/docs/language-basics/src/functions/incr_map.mligo incr_map # Outputs: [ 2 ; 3 ; 4 ] ``` - + ```reasonligo group=c let incr_map = (l : list (int)) : list (int) => List.map ((i : int) => i + 1, l); diff --git a/gitlab-pages/docs/language-basics/loops.md b/gitlab-pages/docs/language-basics/loops.md index 6ae699cd6..ce656ec86 100644 --- a/gitlab-pages/docs/language-basics/loops.md +++ b/gitlab-pages/docs/language-basics/loops.md @@ -7,36 +7,37 @@ title: Loops - + -General iteration in PascaLIGO takes the shape of "while" loops, which -should be familiar to programmers of imperative languages. Those loops -are of the form `while `. Their associated block is -repeatedly evaluated until the condition becomes true, or never -evaluated if the condition is false at the start. The loop never -terminates if the condition never becomes true. Because we are writing -smart contracts on Tezos, when the condition of a "while" loops fails -to become true, the execution will run out of gas and stop with a -failure anyway. +General iteration in PascaLIGO takes the shape of general loops, which +should be familiar to programmers of imperative languages as "while +loops". Those loops are of the form `while `. Their +associated block is repeatedly evaluated until the condition becomes +true, or never evaluated if the condition is false at the start. The +loop never terminates if the condition never becomes true. Because we +are writing smart contracts on Tezos, when the condition of a "while" +loops fails to become true, the execution will run out of gas and stop +with a failure anyway. Here is how to compute the greatest common divisors of two natural number by means of Euclid's algorithm: ```pascaligo group=a -function gcd (var x : nat; var y : nat) : nat is block { - if x < y then - block { - const z : nat = x; - x := y; y := z +function gcd (var x : nat; var y : nat) : nat is + block { + if x < y then + block { + const z : nat = x; + x := y; y := z + } + else skip; + var r : nat := 0n; + while y =/= 0n block { + r := x mod y; + x := y; + y := r } - else skip; - var r : nat := 0n; - while y =/= 0n block { - r := x mod y; - x := y; - y := r - } -} with x + } with x ``` You can call the function `gcd` defined above using the LIGO compiler @@ -64,6 +65,9 @@ to have a special type: if the type of the accumulator is `t`, then it must have the type `bool * t` (not simply `t`). It is the boolean value that denotes whether the stopping condition has been reached. +Here is how to compute the greatest common divisors of two natural +number by means of Euclid's algorithm: + ```cameligo group=a let iter (x,y : nat * nat) : bool * (nat * nat) = if y = 0n then false, (x,y) else true, (y, x mod y) @@ -86,7 +90,6 @@ let gcd (x,y : nat * nat) : nat = let x,y = Loop.fold_while iter (x,y) in x ``` - You can call the function `gcd` defined above using the LIGO compiler like so: ```shell @@ -113,6 +116,9 @@ accumulator is `t`, then it must have the type `bool * t` (not simply `t`). It is the boolean value that denotes whether the stopping condition has been reached. +Here is how to compute the greatest common divisors of two natural +number by means of Euclid's algorithm: + ```reasonligo group=a let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) => if (y == 0n) { (false, (x,y)); } else { (true, (y, x mod y)); }; @@ -139,15 +145,15 @@ let gcd = ((x,y) : (nat, nat)) : nat => { ``` -## For Loops +## Bounded Loops - - +In addition to general loops, PascaLIGO features a specialised kind of +*loop to iterate over bounded intervals*. These loops are familiarly +known as "for loops" and they have the form `for to + `, which is familiar for programmers of +imperative languages. -To iterate over a range of integers you use a loop of the form `for - to `, which is -familiar for programmers of imperative languages. Note that, for the -sake of generality, the bounds are of type `int`, not `nat`. +Consider how to sum integers from `0` to `n`: ```pascaligo group=c function sum (var n : nat) : int is block { @@ -158,6 +164,8 @@ function sum (var n : nat) : int is block { } with acc ``` +(Please do not use that function: there exists a closed form formula.) + You can call the function `sum` defined above using the LIGO compiler like so: ```shell @@ -165,11 +173,6 @@ ligo run-function gitlab-pages/docs/language-basics/src/loops/sum.ligo sum 7n # Outputs: 28 ``` - - - - - PascaLIGO "for" loops can also iterate through the contents of a collection, that is, a list, a set or a map. This is done with a loop @@ -230,4 +233,3 @@ gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_map 'map ["1"->1; "2"->2; "3"->3]' # Outputs: ( "123", 6 ) ``` - diff --git a/gitlab-pages/docs/language-basics/maps-records.md b/gitlab-pages/docs/language-basics/maps-records.md index 50ec7e8b8..59226b4e3 100644 --- a/gitlab-pages/docs/language-basics/maps-records.md +++ b/gitlab-pages/docs/language-basics/maps-records.md @@ -17,7 +17,8 @@ special operator (`.`). Let us first consider and example of record type declaration. - + + ```pascaligo group=a type user is record [ @@ -27,7 +28,7 @@ type user is ] ``` - + ```cameligo group=a type user = { id : nat; @@ -36,7 +37,7 @@ type user = { } ``` - + ```reasonligo group=a type user = { id : nat, @@ -49,7 +50,7 @@ type user = { And here is how a record value is defined: - + ```pascaligo group=a const alice : user = record [ @@ -59,7 +60,7 @@ const alice : user = ] ``` - + ```cameligo group=a let alice : user = { id = 1n; @@ -68,7 +69,7 @@ let alice : user = { } ``` - + ```reasonligo group=a let alice : user = { id : 1n, @@ -80,21 +81,21 @@ let alice : user = { ### Accessing Record Fields -If we want the contents of a given field, we use the `.` infix +If we want the contents of a given field, we use the (`.`) infix operator, like so: - + ```pascaligo group=a const alice_admin : bool = alice.is_admin ``` - + ```cameligo group=a let alice_admin : bool = alice.is_admin ``` - + ```reasonligo group=a let alice_admin: bool = alice.is_admin; ``` @@ -118,7 +119,7 @@ points on a plane. - + ```pascaligo group=b type point is record [x : int; y : int; z : int] type vector is record [dx : int; dy : int] @@ -142,7 +143,7 @@ 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. - + The syntax for the functional updates of record in CameLIGO follows that of OCaml: @@ -156,7 +157,6 @@ let origin : point = {x = 0; y = 0; z = 0} let xy_translate (p, vec : point * vector) : point = {p with x = p.x + vec.dx; y = p.y + vec.dy} ``` - You can call the function `xy_translate` defined above by running the following command of the shell: @@ -171,7 +171,7 @@ xy_translate "({x=2;y=3;z=1}, {dx=3;dy=4})" > functional update: a nameless new version of it has been created and > returned. - + The syntax for the functional updates of record in ReasonLIGO follows that of OCaml: @@ -187,7 +187,7 @@ let xy_translate = ((p, vec) : (point, vector)) : point => ``` -You can call the function `x_translation` defined above by running the +You can call the function `xy_translate` defined above by running the following command of the shell: ```shell ligo run-function @@ -213,9 +213,6 @@ name "patch"). Let us consider defining a function that translates three-dimensional points on a plane. - - - ```pascaligo group=c type point is record [x : int; y : int; z : int] type vector is record [dx : int; dy : int] @@ -273,9 +270,10 @@ 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 { - const p : point = p with record [x = p.x + vec.dx; y = p.y + vec.dy] -} with p +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 ``` You can call the new function `xy_translate` defined above by running the @@ -289,9 +287,6 @@ xy_translate "(record [x=2;y=3;z=1], record [dx=3;dy=4])" The hiding of a variable by another (here `p`) is called `shadowing`. - - - ## Maps *Maps* are a data structure which associate values of the same type to @@ -304,19 +299,19 @@ Here is how a custom map from addresses to a pair of integers is defined. - + ```pascaligo group=f type move is int * int type register is map (address, move) ``` - + ```cameligo group=f type move = int * int type register = (address, move) map ``` - + ```reasonligo group=f type move = (int, int); type register = map (address, move); @@ -326,7 +321,7 @@ type register = map (address, move); And here is how a map value is defined: - + ```pascaligo group=f const moves : register = @@ -340,7 +335,7 @@ const moves : register = > address)` means that we cast a string into an address. Also, `map` > is a keyword. - + ```cameligo group=f let moves : register = Map.literal [ @@ -353,7 +348,7 @@ let moves : register = > separate individual map entries. `("": address)` > means that we type-cast a string into an address. - + ```reasonligo group=f let moves : register = Map.literal ([ @@ -375,19 +370,19 @@ associated to a given key (`address` here) in the register. Here is an example: - + ```pascaligo group=f const my_balance : option (move) = moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] ``` - + ```cameligo group=f let my_balance : move option = Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves ``` - + ```reasonligo group=f let my_balance : option (move) = Map.find_opt (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), moves); @@ -400,7 +395,7 @@ the reader to account for a missing key in the map. This requires - + ```pascaligo group=f function force_access (const key : address; const moves : register) : move is case moves[key] of @@ -409,7 +404,7 @@ function force_access (const key : address; const moves : register) : move is end ``` - + ```cameligo group=f let force_access (key, moves : address * register) : move = match Map.find_opt key moves with @@ -417,7 +412,7 @@ let force_access (key, moves : address * register) : move = | None -> (failwith "No move." : move) ``` - + ```reasonligo group=f let force_access = ((key, moves) : (address, register)) : move => { switch (Map.find_opt (key, moves)) { @@ -438,7 +433,7 @@ those operations are called *updates*. - + The values of a PascaLIGO map can be updated using the usual assignment syntax `[] := `. Let us @@ -464,7 +459,7 @@ function assignments (var m : register) : register is } with m ``` - + We can update a binding in a map in CameLIGO by means of the `Map.update` built-in function: @@ -479,7 +474,7 @@ let assign (m : register) : register = > use `None` instead, that would have meant that the binding is > removed. - + We can update a binding in a map in ReasonLIGO by means of the `Map.update` built-in function: @@ -501,7 +496,7 @@ To remove a binding from a map, we need its key. - + In PascaLIGO, there is a special instruction to remove a binding from a map. @@ -512,13 +507,19 @@ function delete (const key : address; var moves : register) : register is } with moves ``` - + + +In CameLIGO, we use the predefined function `Map.remove` as follows: + ```cameligo group=f let delete (key, moves : address * register) : register = Map.remove key moves ``` - + + +In ReasonLIGO, we use the predefined function `Map.remove` as follows: + ```reasonligo group=f let delete = ((key, moves) : (address, register)) : register => { Map.remove (key, moves); @@ -528,51 +529,85 @@ let delete = ((key, moves) : (address, register)) : register => { -### Iterating Functionally over a Map +### Functional Iteration over Maps 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). -There are three kinds of functional iteration over LIGO maps: `iter`, -`map` and `fold`. The first, `iter`, is an iteration over the map with +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*. + +#### Iterated Operation + +The first, the *iterated operation*, is an iteration over the map with 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. - + + + +In PascaLIGO, 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`. + ```pascaligo group=f function iter_op (const m : register) : unit is block { - function aggregate (const i : address; const j : move) : unit is block - { if j.1 > 1 then skip else failwith ("Below range.") } with unit - } with map_iter (aggregate, m) + 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) ``` - +> The iterated function must be pure, that is, it cannot mutate +> variables. + + + +In CameLIGO, the predefinded 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`. + ```cameligo group=f let iter_op (m : register) : unit = - let assert_eq = fun (i,j : address * move) -> assert (j.0 > 1) - in Map.iter assert_eq m + let predicate = fun (i,j : address * move) -> assert (j.0 > 3) + in Map.iter predicate m ``` - + + +In ReasonLIGO, 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`. + ```reasonligo group=f let iter_op = (m : register) : unit => { - let assert_eq = ((i,j) : (address, move)) => assert (j[0] > 1); - Map.iter (assert_eq, m); + let predicate = ((i,j) : (address, move)) => assert (j[0] > 3); + Map.iter (predicate, m); }; ``` +#### Map Operation + We may want to change all the bindings of a map by applying to them a -function. This is also called a *map operation*, as opposed to the -*map data structure* we have been presenting. +function. This is called a *map operation*, not to be confused with +the map data structure. - + + + +In PascaLIGO, the predefined functional iterator implementing the map +operation over maps is called `map_map`and is used as follows: + ```pascaligo group=f function map_op (const m : register) : register is block { @@ -581,49 +616,78 @@ function map_op (const m : register) : register is } with map_map (increment, m) ``` - +> The mapped function must be pure, that is, it cannot mutate +> variables. + + + +In CameLIGO, the predefined functional iterator implementing the map +operation over maps is called `Map.map` and is used as follows: + ```cameligo group=f let map_op (m : register) : register = let increment = fun (i,j : address * move) -> j.0, j.1 + 1 in Map.map increment m ``` - + + +In ReasonLIGO, the predefined functional iteratir implementing the map +operation over maps is called `Map.map` and is used as follows: + ```reasonligo group=f let map_op = (m : register) : register => { let increment = ((i,j): (address, move)) => (j[0], j[1] + 1); - Map.map(increment, m); + Map.map (increment, m); }; ``` -A *fold operation* is the most general of iterations. The iterated +#### Fold Operation + +A *fold operation* is the most general of iterations. The folded function takes two arguments: an *accumulator* and the structure *element* at hand, with which it then produces a new accumulator. This enables having a partial result that becomes complete when the traversal of the data structure is over. - + + + +In PascaLIGO, the predefined functional iterator implementing the fold +operation over maps is called `map_fold` and is used as follows: + ```pascaligo group=f function fold_op (const m : register) : int is block { - function aggregate (const j : int; const cur : address * move) : int is + function iterated (const j : int; const cur : address * move) : int is j + cur.1.1 - } with map_fold (aggregate, m, 5) + } with map_fold (iterated, m, 5) ``` - +> The folded function must be pure, that is, it cannot mutate +> variables. + + + +In CameLIGO, the predefined functional iterator implementing the fold +operation over maps is called `Map.fold` and is used as follows: + ```cameligo group=f let fold_op (m : register) : register = - let aggregate = fun (i,j : int * (address * move)) -> i + j.1.1 - in Map.fold aggregate m 5 + let iterated = fun (i,j : int * (address * move)) -> i + j.1.1 + in Map.fold iterated m 5 ``` - + + +In ReasonLIGO, the predefined functional iterator implementing the +fold operation over maps is called `Map.fold` and is used as follows: + ```reasonligo group=f let fold_op = (m : register) : register => { - let aggregate = ((i,j): (int, (address, move))) => i + j[1][1]; - Map.fold (aggregate, m, 5); + let iterated = ((i,j): (int, (address, move))) => i + j[1][1]; + Map.fold (iterated, m, 5); }; ``` @@ -643,32 +707,32 @@ interface for big maps is analogous to the one used for ordinary maps. Here is how we define a big map: - + ```pascaligo group=g type move is int * int type register is big_map (address, move) ``` - + ```cameligo group=g type move = int * int type register = (address, move) big_map ``` - + ```reasonligo group=g type move = (int, int); -type register = big_map(address, move); +type register = big_map (address, move); ``` And here is how a map value is created: - + ```pascaligo group=g const moves : register = @@ -682,7 +746,7 @@ const moves : register = > `("" : address)` means that we cast a string into an > address. - + ```cameligo group=g let moves : register = @@ -696,7 +760,7 @@ let moves : register = > separating individual map entries. The annotated value `(" value>" : address)` means that we cast a string into an address. - + ```reasonligo group=g let moves : register = @@ -713,28 +777,30 @@ let moves : register = -### Accessing Values by Key +### Accessing Values If we want to access a move from our `register` above, we can use the postfix `[]` operator to read the associated `move` value. However, -the value we read is an optional value: in our case, of type `option -(move)`. Here is an example: +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: - + + + ```pascaligo group=g const my_balance : option (move) = moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] ``` - + ```cameligo group=g let my_balance : move option = Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves ``` - + ```reasonligo group=g let my_balance : option (move) = @@ -742,23 +808,25 @@ let my_balance : option (move) = ``` -### Updating a Big Map +### Updating Big Maps - + The values of a PascaLIGO big map can be updated using the assignment syntax for ordinary maps ```pascaligo group=g -function assign (var m : register) : register is +function add (var m : register) : register is block { m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9) } with m + +const updated_map : register = add (moves) ``` - + We can update a big map in CameLIGO using the `Big_map.update` built-in: @@ -769,7 +837,7 @@ let updated_map : register = ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) moves ``` - + We can update a big map in ReasonLIGO using the `Big_map.update` built-in: @@ -780,6 +848,47 @@ let updated_map : register = (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); ``` -### Removing Bindings from a Map + + +### Removing Bindings + +Removing a binding in a map is done differently according to the LIGO +syntax. + + + + + +PascaLIGO features a special syntactic construct to remove bindings +from maps, of the form `remove from map `. For example, + +```pascaligo group=g +function rem (var m : register) : register is + block { + remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) from map moves + } with m + +const updated_map : register = rem (moves) +``` + + + +In CameLIGO, the predefined function which removes a binding in a map +is called `Map.remove` and is used as follows: + +```cameligo group=g +let updated_map : register = + Map.remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves +``` + + + +In ReasonLIGO, the predefined function which removes a binding in a map +is called `Map.remove` and is used as follows: + +```reasonligo group=g +let updated_map : register = + Map.remove (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves) +``` diff --git a/gitlab-pages/docs/language-basics/math-numbers-tez.md b/gitlab-pages/docs/language-basics/math-numbers-tez.md index bb274cd7c..11a589c12 100644 --- a/gitlab-pages/docs/language-basics/math-numbers-tez.md +++ b/gitlab-pages/docs/language-basics/math-numbers-tez.md @@ -18,8 +18,7 @@ adding a value of type `int` to a value of type `tez` is invalid. Note that adding an integer to a natural number produces an integer. - - + ```pascaligo group=a // int + int yields int const a : int = 5 + 10 @@ -50,7 +49,6 @@ const g : int = 1_000_000 >``` - ```cameligo group=a // int + int yields int let a : int = 5 + 10 @@ -81,7 +79,6 @@ let g : int = 1_000_000 >``` - ```reasonligo group=a // int + int yields int let a : int = 5 + 10; @@ -99,7 +96,7 @@ let c : tez = 5mutez + 10mutez; let e : nat = 5n + 10n; // nat + int yields an int: invalid -//let f : nat = 5n + 10; +// let f : nat = 5n + 10; let g : int = 1_000_000; ``` @@ -109,7 +106,6 @@ let g : int = 1_000_000; >```reasonligo >let sum : tex = 100_000mutez; >``` - ## Subtraction @@ -119,7 +115,7 @@ Subtraction looks as follows. > ⚠️ Even when subtracting two `nats`, the result is an `int` - + ```pascaligo group=b const a : int = 5 - 10 @@ -166,7 +162,7 @@ let d : tez = 5mutez - 1mutez; You can multiply values of the same type, such as: - + ```pascaligo group=c const a : int = 5 * 5 @@ -200,7 +196,7 @@ In LIGO you can divide `int`, `nat`, and `tez`. Here is how: > ⚠️ Division of two `tez` values results into a `nat` - + ```pascaligo group=d const a : int = 10 / 3 const b : nat = 10n / 3n @@ -248,11 +244,11 @@ let b : nat = abs (1); -## Check if a value is a `nat` +## Checking a `nat` -You can check if a value is a `nat` by using a syntax specific -built-in function, which accepts an `int` and returns an optional -`nat`: if `Some(nat)` then the provided integer was indeed a natural +You can check if a value is a `nat` by using a predefined cast +function which accepts an `int` and returns an optional `nat`: if the +result is not `None`, then the provided integer was indeed a natural number, and not otherwise. diff --git a/gitlab-pages/docs/language-basics/sets-lists-tuples.md b/gitlab-pages/docs/language-basics/sets-lists-tuples.md index e43de8200..8da5b6300 100644 --- a/gitlab-pages/docs/language-basics/sets-lists-tuples.md +++ b/gitlab-pages/docs/language-basics/sets-lists-tuples.md @@ -3,11 +3,8 @@ id: sets-lists-tuples title: Tuples, Lists, Sets --- -Apart from complex data types such as `maps` and `records`, ligo also -exposes `tuples`, `lists` and `sets`. - -> ⚠️ Make sure to pick the appropriate data type for your use case, and -> bear in mind the related gas costs. +Apart from complex data types such as `maps` and `records`, LIGO also +features `tuples`, `lists` and `sets`. ## Tuples @@ -15,16 +12,16 @@ Tuples gather a given number of values in a specific order and those values, called *components*, can be retrieved by their index (position). Probably the most common tuple is the *pair*. For example, if we were storing coordinates on a two dimensional grid we -might use a pair of type `int * int` to store the coordinates `x` and -`y` as the pair value `(x,y)`. There is a *specific order*, so `(y,x)` -is not equal to `(x,y)`. The number of components is part of the type -of a tuple, so, for example, we cannot add an extra component to a -pair and obtain a triple of the same type: `(x,y)` has always a -different type from `(x,y,z)`, whereas `(y,x)` may have the same type. +might use a pair `(x,y)` to store the coordinates `x` and `y`. There +is a *specific order*, so `(y,x)` is not equal to `(x,y)`. The number +of components is part of the type of a tuple, so, for example, we +cannot add an extra component to a pair and obtain a triple of the +same type, so, for instance, `(x,y)` has always a different type from +`(x,y,z)`, whereas `(y,x)` might have the same type as `(x,y)`. Like records, tuple components can be of arbitrary types. -### Defining a tuple +### Defining Tuples Unlike [a record](language-basics/maps-records.md), tuple types do not have to be defined before they can be used. However below we will give @@ -33,54 +30,55 @@ them names by *type aliasing*. -```pascaligo group=c + +```pascaligo group=tuple type full_name is string * string // Alias const full_name : full_name = ("Alice", "Johnson") ``` -```cameligo group=c + +```cameligo group=tuple type full_name = string * string // Alias -(* The parenthesis here are optional *) -let full_name : full_name = ("Alice", "Johnson") +let full_name : full_name = ("Alice", "Johnson") // Optional parentheses ``` -```reasonligo group=c + +```reasonligo group=tuple type full_name = (string, string); // Alias -(* The parenthesis here are optional *) let full_name : full_name = ("Alice", "Johnson"); ``` -### Accessing an Element in a Tuple +### Accessing Components Accessing the components of a tuple in OCaml is achieved by [pattern matching](language-basics/unit-option-pattern-matching.md). LIGO currently supports tuple patterns only in the parameters of functions, -not in pattern matching. In LIGO, however, we can access components by -their position in their tuple, which cannot be done in OCaml. +not in pattern matching. However, we can access components by their +position in their tuple, which cannot be done in OCaml. - + Tuple components are one-indexed like so: -```pascaligo group=c +```pascaligo group=tuple const first_name : string = full_name.1; ``` - + Tuple elements are zero-indexed and accessed like so: -```cameligo group=c +```cameligo group=tuple let first_name : string = full_name.0 ``` @@ -88,10 +86,12 @@ let first_name : string = full_name.0 Tuple components are one-indexed like so: -```reasonligo group=c +```reasonligo group=tuple let first_name : string = full_name[1]; ``` + + ## Lists Lists are linear collections of elements of the same type. Linear @@ -105,105 +105,183 @@ think of a list a *stack*, where the top is written on the left. > 💡 Lists are useful when returning operations from a smart > contract's entrypoint. -### Defining a List +### Defining Lists - -```pascaligo group=b + +```pascaligo group=lists const my_list : list (int) = list [1; 2; 2] // The head is 1 ``` -```cameligo group=b +```cameligo group=lists let my_list : int list = [1; 2; 2] // The head is 1 ``` -```reasonligo group=b +```reasonligo group=lists let my_list : list (int) = [1, 2, 2]; // The head is 1 ``` -### Adding to a List +### Adding to Lists Lists can be augmented by adding an element before the head (or, in terms of stack, by *pushing an element on top*). This operation is usually called *consing* in functional languages. + + + + In PascaLIGO, the *cons operator* is infix and noted `#`. It is not symmetric: on the left lies the element to cons, and, on the right, a list on which to cons. (The symbol is helpfully asymmetric to remind you of that.) +```pascaligo group=lists +const larger_list : list (int) = 5 # my_list // [5;1;2;2] +``` + + + In CameLIGO, the *cons operator* is infix and noted `::`. It is not symmetric: on the left lies the element to cons, and, on the right, a list on which to cons. +```cameligo group=lists +let larger_list : int list = 5 :: my_list // [5;1;2;2] +``` + + + In ReasonLIGO, the *cons operator* is infix and noted `, ...`. It is not symmetric: on the left lies the element to cons, and, on the right, a list on which to cons. - - -```pascaligo group=b -const larger_list : list (int) = 5 # my_list -``` - - -```cameligo group=b -let larger_list : int list = 5 :: my_list -``` - - -```reasonligo group=b -let larger_list : list (int) = [5, ...my_list]; +```reasonligo group=lists +let larger_list : list (int) = [5, ...my_list]; // [5,1,2,2] ``` -
- > 💡 Lists can be iterated, folded or mapped to different values. You > can find additional examples > [here](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts) > and other built-in operators > [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L59) -### Mapping of a List +### Functional Iteration over Lists -We may want to apply a function to all the elements of a list and -obtain the resulting list, in the same order. For example, we may want -to create a list that contains all the elements of another list -incremented by one. This is a special case of *fold operation* called -a *map operation*. Map operations (not to be confused by the -[map data structure](language-basics/maps-records.md)), are predefined -functions in LIGO. They take as a parameter the function to apply to -all the elements. Of course, that function must return a value of the -same type as the element. +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). -In PascaLIGO, the map function is called `list_map`. +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*. -In CameLIGO and ReasonLIGO, the map function is called `List.map`. +#### Iterated Operation + +The first, the *iterated operation*, is an iteration over the map with +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. - -```pascaligo group=b + + + +In PascaLIGO, the predefined functional iterator implementing the +iterated operation over lists is called `list_iter`. + +In the following example, a list is iterated to check that all its +elements (integers) are greater than `3`: + +```pascaligo group=lists +function iter_op (const l : list (int)) : unit is + block { + function iterated (const i : int) : unit is + if i > 2 then Unit else (failwith ("Below range.") : unit) + } with list_iter (iterated, l) +``` + +> The iterated function must be pure, that is, it cannot mutate +> variables. + + + +In CameLIGO, the predefined functional iterator implementing the +iterated operation over lists is called `List.iter`. + +In the following example, a list is iterated to check that all its +elements (integers) are greater than `3`: + +```cameligo group=lists +let iter_op (l : int list) : unit = + let predicate = fun (i : int) -> assert (i > 3) + in List.iter predicate l +``` + + + +In ReasonLIGO, the predefined functional iterator implementing the +iterated operation over lists is called `List.iter`. + +In the following example, a list is iterated to check that all its +elements (integers) are greater than `3`: + +```reasonligo group=lists +let iter_op = (l : list (int)) : unit => { + let predicate = (i : int) => assert (i > 3); + List.iter (predicate, l); +}; +``` + + + + +#### Map Operation + +We may want to change all the elements of a given list by applying to +them a function. This is called a *map operation*, not to be confused +with the map data structure. + + + + + +In PascaLIGO, the predefined functional iterator implementing the map +operation over lists is called `list_map` and is used as follows: + +```pascaligo group=lists function increment (const i : int): int is i + 1 // Creates a new list with all elements incremented by 1 const plus_one : list (int) = list_map (increment, larger_list) ``` + -```cameligo group=b + +In CameLIGO, the predefined functional iterator implementing the map +operation over lists is called `List.map` and is used as follows: + +```cameligo group=lists let increment (i : int) : int = i + 1 // Creates a new list with all elements incremented by 1 let plus_one : int list = List.map increment larger_list ``` + -```reasonligo group=b + +In ReasonLIGO, the predefined functional iterator implementing the map +operation over lists is called `List.map` and is used as follows: + +```reasonligo group=lists let increment = (i : int) : int => i + 1; // Creates a new list with all elements incremented by 1 @@ -212,33 +290,51 @@ let plus_one : list (int) = List.map (increment, larger_list); -### Folding of over a List +#### Fold Operation + +A *fold operation* is the most general of iterations. The folded +function takes two arguments: an *accumulator* and the structure +*element* at hand, with which it then produces a new accumulator. This +enables having a partial result that becomes complete when the +traversal of the data structure is over. - -```pascaligo group=b + + + +In PascaLIGO, the predefined functional iterator implementing the fold +operation over lists is called `list_fold` and is used as follows: + +```pascaligo group=lists function sum (const acc : int; const i : int): int is acc + i const sum_of_elements : int = list_fold (sum, my_list, 0) ``` +> The folded function must be pure, that is, it cannot mutate +> variables. + -```cameligo group=b +In CameLIGO, the predefined functional iterator implementing the fold +operation over lists is called `List.fold` and is used as follows: + +```cameligo group=lists let sum (acc, i: int * int) : int = acc + i let sum_of_elements : int = List.fold sum my_list 0 ``` -```reasonligo group=b +In ReasonLIGO, the predefined functional iterator implementing the +fold operation over lists is called `List.fold` and is used as follows: + +```reasonligo group=lists let sum = ((result, i): (int, int)): int => result + i; let sum_of_elements : int = List.fold (sum, my_list, 0); ``` - - ## Sets Sets are unordered collections of values of the same type, like lists @@ -249,56 +345,65 @@ whereas they can be repeated in a list. ### Empty Sets - -```pascaligo group=a + + + +In PascaLIGO, the notation for sets is similar to that for lists, +except the keyword `set` is used before: + +```pascaligo group=sets const my_set : set (int) = set [] ``` -```cameligo group=a -let my_set : int set = (Set.empty : int set) + +In CameLIGO, the empty set is denoted by the predefined value +`Set.empty`. + +```cameligo group=sets +let my_set : int set = Set.empty ``` + -```reasonligo group=a -let my_set : set (int) = (Set.empty : set (int)); + +In CameLIGO, the empty set is denoted by the predefined value +`Set.empty`. + +```reasonligo group=sets +let my_set : set (int) = Set.empty; ``` ### Non-empty Sets + + In PascaLIGO, the notation for sets is similar to that for lists, except the keyword `set` is used before: - - -```pascaligo group=a +```pascaligo group=sets const my_set : set (int) = set [3; 2; 2; 1] ``` - - You can check that `2` is not repeated in `my_set` by using the LIGO compiler like this (the output will sort the elements of the set, but that order is not significant for the compiler): - ```shell ligo evaluate-value gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo my_set # Outputs: { 3 ; 2 ; 1 } ``` + + In CameLIGO, there is no predefined syntactic construct for sets: you must build your set by adding to the empty set. (This is the way in OCaml.) - - -```cameligo group=a +```cameligo group=sets let my_set : int set = Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set)))) ``` - - You can check that `2` is not repeated in `my_set` by using the LIGO compiler like this (the output will sort the elements of the set, but that order is not significant for the compiler): @@ -309,17 +414,16 @@ gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo my_set # Outputs: { 3 ; 2 ; 1 } ``` + + In ReasonLIGO, there is no predefined syntactic construct for sets: you must build your set by adding to the empty set. (This is the way in OCaml.) - - -```reasonligo group=a +```reasonligo group=sets let my_set : set (int) = Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int))))); ``` - You can check that `2` is not repeated in `my_set` by using the LIGO compiler like this (the output will sort the elements of the set, but @@ -330,25 +434,36 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo my_set # Outputs: { 3 ; 2 ; 1 } ``` + ### Set Membership + + -PascaLIGO features a special keyword `constains` that operates like an +PascaLIGO features a special keyword `contains` that operates like an infix operator checking membership in a set. - - -```pascaligo group=a +```pascaligo group=sets const contains_3 : bool = my_set contains 3 ``` + -```cameligo group=a + +In CameLIGO, the predefined predicate `Set.mem` tests for membership +in a set as follows: + +```cameligo group=sets let contains_3 : bool = Set.mem 3 my_set ``` + -```reasonligo group=a + +In ReasonLIGO, the predefined predicate `Set.mem` tests for membership +in a set as follows: + +```reasonligo group=sets let contains_3 : bool = Set.mem (3, my_set); ``` @@ -357,37 +472,50 @@ let contains_3 : bool = Set.mem (3, my_set); ### Cardinal - -```pascaligo group=a + + + +In PascaLIGO, the predefined function `size` returns the number of +elements in a given set as follows: + +```pascaligo group=sets const set_size : nat = size (my_set) ``` -```cameligo group=a + +In CameLIGO, the predefined function `Set.size` returns the number of +elements in a given set as follows: + +```cameligo group=sets let set_size : nat = Set.size my_set ``` -```reasonligo group=a + +In ReasonLIGO, the predefined function `Set.size` returns the number +of elements in a given set as follows: + +```reasonligo group=sets let set_size : nat = Set.size (my_set); ``` + -### Adding or Removing from a Set - - - -In PascaLIGO, there are two ways to update a set. Either we create a -new set from the given one, or we modify it in-place. First, let us -consider the former: +### Updating Sets - -```pascaligo group=a + + + +In PascaLIGO, there are two ways to update a set, that is to add or +remove from it. Either we create a new set from the given one, or we +modify it in-place. First, let us consider the former way + +```pascaligo group=sets const larger_set : set (int) = set_add (4, my_set) const smaller_set : set (int) = set_remove (3, my_set) ``` - If we are in a block, we can use an instruction to modify the set bound to a given variable. This is called a *patch*. It is only @@ -400,37 +528,31 @@ this instruction is equivalent to perform the union of two sets, one that is modified in-place, and the other given as a literal (extensional definition). - - -```pascaligo group=a +```pascaligo group=sets function update (var s : set (int)) : set (int) is block { patch s with set [4; 7] } with s const new_set : set (int) = update (my_set) ``` - + In CameLIGO, we update a given set by creating another one, with or without some elements. - - -```cameligo group=a +```cameligo group=sets let larger_set : int set = Set.add 4 my_set let smaller_set : int set = Set.remove 3 my_set ``` - + In ReasonLIGO, we update a given set by creating another one, with or without some elements. - - -```reasonligo group=a +```reasonligo group=sets let larger_set : set (int) = Set.add (4, my_set); let smaller_set : set (int) = Set.remove (3, my_set); @@ -438,32 +560,149 @@ let smaller_set : set (int) = Set.remove (3, my_set); -### Folding over a Set +### Functional Iteration over Sets +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). -Given a set, we may want to apply a function in turn to all the -elements it contains, while accumulating some value which is returned -at the end. This is a *fold operation*. In the following example, we -sum up all the elements of the set `my_set` defined above. +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*. +#### Iterated Operation -In PascaLIGO, the folded function takes the accumulator first and the -(current) set element second. The predefined fold is called `set_fold`. +The first, the *iterated operation*, is an iteration over the map with +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. - -```pascaligo group=a + + + +In PascaLIGO, the predefined functional iterator implementing the +iterated operation over sets is called `set_iter`. + +In the following example, a set is iterated to check that all its +elements (integers) are greater than `3`: + +```pascaligo group=sets +function iter_op (const s : set (int)) : unit is + block { + function iterated (const i : int) : unit is + if i > 2 then Unit else (failwith ("Below range.") : unit) + } with set_iter (iterated, s) +``` + +> The iterated function must be pure, that is, it cannot mutate +> variables. + + + +In CameLIGO, the predefined functional iterator implementing the +iterated operation over sets is called `Set.iter`. + +In the following example, a set is iterated to check that all its +elements (integers) are greater than `3`: + +```cameligo group=sets +let iter_op (s : int set) : unit = + let predicate = fun (i : int) -> assert (i > 3) + in Set.iter predicate s +``` + + + +In ReasonLIGO, the predefined functional iterator implementing the +iterated operation over sets is called `Set.iter`. + +In the following example, a set is iterated to check that all its +elements (integers) are greater than `3`: + +```reasonligo group=sets +let iter_op = (s : set (int)) : unit => { + let predicate = (i : int) => assert (i > 3); + Set.iter (predicate, s); +}; +``` + + + + +#### Map Operation + +We may want to change all the elements of a given set by applying to +them a function. This is called a *map operation*, not to be confused +with the map data structure. + + + + + +In PascaLIGO, the predefined functional iterator implementing the map +operation over sets is called `set_map` and is used as follows: + +```pascaligo group=sets +function increment (const i : int): int is i + 1 + +// Creates a new set with all elements incremented by 1 +const plus_one : set (int) = set_map (increment, larger_set) +``` + + + +In CameLIGO, the predefined functional iterator implementing the map +operation over sets is called `Set.map` and is used as follows: + +```cameligo group=sets +let increment (i : int) : int = i + 1 + +// Creates a new set with all elements incremented by 1 +let plus_one : int set = Set.map increment larger_set +``` + + + +In ReasonLIGO, the predefined functional iterator implementing the map +operation over sets is called `Set.map` and is used as follows: + +```reasonligo group=sets +let increment = (i : int) : int => i + 1; + +// Creates a new set with all elements incremented by 1 +let plus_one : set (int) = Set.map (increment, larger_set); +``` + + +#### Fold Operation + +A *fold operation* is the most general of iterations. The folded +function takes two arguments: an *accumulator* and the structure +*element* at hand, with which it then produces a new accumulator. This +enables having a partial result that becomes complete when the +traversal of the data structure is over. + + + + + +In PascaLIGO, the predefined functional iterator implementing the fold +operation over sets is called `set_fold` and is used as follows: + +```pascaligo group=sets function sum (const acc : int; const i : int): int is acc + i const sum_of_elements : int = set_fold (sum, my_set, 0) ``` - + +> The folded function must be pure, that is, it cannot mutate +> variables. It is possible to use a *loop* over a set as well. - - -```pascaligo group=a +```pascaligo group=sets function loop (const s : set (int)) : int is block { var sum : int := 0; for element in set s block { @@ -471,27 +710,24 @@ function loop (const s : set (int)) : int is block { } } with sum ``` - + + In CameLIGO, the predefined fold over sets is called `Set.fold`. - - -```cameligo group=a +```cameligo group=sets let sum (acc, i : int * int) : int = acc + i let sum_of_elements : int = Set.fold sum my_set 0 ``` - + + In ReasonLIGO, the predefined fold over sets is called `Set.fold`. - - -```reasonligo group=a +```reasonligo group=sets let sum = ((acc, i) : (int, int)) : int => acc + i; let sum_of_elements : int = Set.fold (sum, my_set, 0); ``` - diff --git a/gitlab-pages/docs/language-basics/strings.md b/gitlab-pages/docs/language-basics/strings.md index 297edee09..3ad0d568d 100644 --- a/gitlab-pages/docs/language-basics/strings.md +++ b/gitlab-pages/docs/language-basics/strings.md @@ -6,7 +6,7 @@ title: Strings Strings are defined using the built-in `string` type like this: - + ``` const a : string = "Hello Alice" ``` @@ -21,10 +21,10 @@ let a : string = "Hello Alice"; -## Concatenating strings +## Concatenating Strings - + Strings can be concatenated using the `^` operator. ```pascaligo @@ -51,12 +51,12 @@ let full_greeting : string = greeting ++ " " ++ name; -## Slicing strings +## Slicing Strings Strings can be sliced using a built-in function: - + ```pascaligo const name : string = "Alice" const slice : string = string_slice (0n, 1n, name) @@ -73,29 +73,27 @@ let slice : string = String.slice (0n, 1n, name); ``` -> ⚠️ Notice that the `offset` and slice `length` are natural numbers -> (`nat`). +> ⚠️ Notice that the offset and length of the slice are natural numbers. -## Aquiring the length of a string +## Length of Strings The length of a string can be found using a built-in function: - + ```pascaligo const name : string = "Alice" -// length = 5 -const length : nat = size (name) +const length : nat = size (name) // length = 5 ``` ```cameligo let name : string = "Alice" -let length : nat = String.size name +let length : nat = String.size name // length = 5 ``` ```reasonligo let name : string = "Alice"; -let length : nat = String.size (name); +let length : nat = String.size (name); // length == 5 ``` diff --git a/gitlab-pages/docs/language-basics/tezos-specific.md b/gitlab-pages/docs/language-basics/tezos-specific.md index a1f8c17dc..bd50a4229 100644 --- a/gitlab-pages/docs/language-basics/tezos-specific.md +++ b/gitlab-pages/docs/language-basics/tezos-specific.md @@ -7,7 +7,7 @@ LIGO is a programming language for writing Tezos smart contracts. It would be a little odd if it did not have any Tezos specific functions. This page will tell you about them. -## Pack and unpack +## Pack and Unpack Michelson provides the `PACK` and `UNPACK` instructions for data serialization. The instruction `PACK` converts Michelson data @@ -59,11 +59,12 @@ predefined function returning a value of type `key_hash`. ```pascaligo group=b -function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is block { - var ret : bool := False; - var kh2 : key_hash := crypto_hash_key (k2); - if kh1 = kh2 then ret := True else skip -} with (ret, kh2) +function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is + block { + var ret : bool := False; + var kh2 : key_hash := crypto_hash_key (k2); + if kh1 = kh2 then ret := True else skip + } with (ret, kh2) ``` @@ -122,14 +123,14 @@ let check_signature = -## Getting the contract's own address +## Contract's Own Address Often you want to get the address of the contract being executed. You can do it with `self_address`. > ⚠️ Due to limitations in Michelson, `self_address` in a contract is -> only allowed at the entry-point level (a.k.a top-level). Using it in -> an auxiliaru function will cause an error. +> only allowed at the entrypoint level, that is, at the +> top-level. Using it in an embedded function will cause an error. diff --git a/gitlab-pages/docs/language-basics/types.md b/gitlab-pages/docs/language-basics/types.md index 6654e7a3b..ace3e8794 100644 --- a/gitlab-pages/docs/language-basics/types.md +++ b/gitlab-pages/docs/language-basics/types.md @@ -3,10 +3,10 @@ id: types title: Types --- -LIGO is strongly and statically typed. This means that the compiler -checks your program at compilation time and, if it passes the tests, -this ensures that there will be no runtime error due to wrong -assumptions on the data. This is called *type checking*. +*LIGO is strongly and statically typed.* This means that the compiler +checks how your contract processes data. If it passes the test, your +contract will not fail at run-time due to inconsistent assumptions on +your data. This is called *type checking*. LIGO types are built on top of Michelson's type system. @@ -23,7 +23,7 @@ alias a string type as an animal breed - this will allow us to comunicate our intent with added clarity. - + ```pascaligo group=a type breed is string const dog_breed : breed = "Saluki" @@ -51,15 +51,14 @@ let dog_breed : breed = "Saluki"; ## Simple types - + ```pascaligo group=b // The type accountBalances denotes maps from addresses to tez type account_balances is map (address, tez) const ledger : account_balances = - map - [("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez] + map [("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez] ``` @@ -99,19 +98,22 @@ below you can see the definition of data types for a ledger that keeps the balance and number of previous transactions for a given account. - + ```pascaligo group=c // Type aliasing + type account is address type number_of_transactions is nat // The type account_data is a record with two fields. + type account_data is record [ balance : tez; transactions : number_of_transactions ] // A ledger is a map from accounts to account_data + type ledger is map (account, account_data) const my_ledger : ledger = map [ @@ -126,14 +128,19 @@ const my_ledger : ledger = map [ ```cameligo group=c // Type aliasing + type account = address type number_of_transactions = nat + // The type account_data is a record with two fields. + type account_data = { balance : tez; transactions : number_of_transactions } + // A ledger is a map from accounts to account_data + type ledger = (account, account_data) map let my_ledger : ledger = Map.literal @@ -144,16 +151,19 @@ let my_ledger : ledger = Map.literal ```reasonligo group=c // Type aliasing + type account = address; type number_of_transactions = nat; // The type account_data is a record with two fields. + type account_data = { balance : tez, transactions : number_of_transactions }; // A ledger is a map from accounts to account_data + type ledger = map (account, account_data); let my_ledger : ledger = @@ -173,19 +183,91 @@ exclusive to each other). ## Annotations In certain cases, the type of an expression cannot be properly -inferred by the compiler. In order to help the type checke, you can +inferred by the compiler. In order to help the type checker, you can annotate an expression with its desired type. Here is an example: - -```pascaligo -type int_map is map (int, int) -function get_first (const my_map : int_map): option (int) is my_map[1] + +```pascaligo group=d +type parameter is Back | Claim | Withdraw -// The empty map always needs a type annotation +type storage is + record + owner : address; + goal : tez; + deadline : timestamp; + backers : map (address, tez); + funded : bool + end -const first : option (int) = get_first (((map end) : int_map)) +type return is list (operation) * storage + +function back (var action : unit; var store : storage) : return is + begin + if now > store.deadline then + failwith ("Deadline passed.") + else case store.backers[sender] of + None -> store.backers[sender] := amount + | Some (x) -> skip + end + end with ((nil : list (operation)), store) // Annotation +``` + + +```cameligo group=d +type parameter = Back | Claim | Withdraw + +type storage = { + owner : address; + goal : tez; + deadline : timestamp; + backers : (address, tez) map; + funded : bool +} + +type return = operation list * storage + +let back (param, store : unit * storage) : return = + let no_op : operation list = [] in + if Current.time > store.deadline then + (failwith "Deadline passed." : return) // Annotation + else + match Map.find_opt sender store.backers with + None -> + let backers = Map.update sender (Some amount) store.backers + in no_op, {store with backers=backers} + | Some (x) -> no_op, store +``` + + +```reasonligo group=d +type parameter = | Back | Claim | Withdraw; + +type storage = { + owner : address, + goal : tez, + deadline : timestamp, + backers : map (address, tez), + funded : bool, +}; + +type return = (list (operation), storage); + +let back = ((param, store) : (unit, storage)) : return => { + let no_op : list (operation) = []; + if (Current.time > store.deadline) { + (failwith ("Deadline passed.") : return); // Annotation + } + else { + switch (Map.find_opt (sender, store.backers)) { + | None => { + let backers = Map.update (sender, Some (amount), store.backers); + (no_op, {...store, backers:backers}) } + | Some (x) => (no_op, store) + } + } +}; ``` diff --git a/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md b/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md index 3243750cc..ff497a6b3 100644 --- a/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md +++ b/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md @@ -10,21 +10,18 @@ features it as well. Both the option type and the unit types are instances of a more general kind of types: *variant types* (sometimes called *sum types*). -## The unit type +## The unit Type The `unit` type in Michelson or LIGO is a predefined type that contains only one value that carries no information. It is used when no relevant information is required or produced. Here is how it used. -> 💡 Units come in handy when we try pattern matching on custom -> variants below. - - + In PascaLIGO, the unique value of the `unit` type is `Unit`. ```pascaligo group=a -const n : unit = Unit +const n : unit = Unit // Note the capital letter ``` @@ -56,11 +53,11 @@ Here is how we define a coin as being either head or tail (and nothing else): - + ```pascaligo group=b type coin is Head | Tail -const head : coin = Head (Unit) // Unit needed because of a bug -const tail : coin = Tail (Unit) // Unit needed because of a bug +const head : coin = Head (Unit) // Unit needed for now. +const tail : coin = Tail (Unit) // Unit needed for now. ``` @@ -87,7 +84,7 @@ define different kinds of users of a system. - + ```pascaligo group=c type id is nat @@ -128,9 +125,6 @@ let g : user = Guest; -Defining a variant can be extremely useful for building semantically -appealing contracts. We will learn how to use variants for "logic -purposes"' shortly. ## Optional values @@ -143,7 +137,7 @@ meaningful value *of any type*. An example in arithmetic is the division operation: - + ```pascaligo group=d function div (const a : nat; const b : nat) : option (nat) is if b = 0n then (None: option (nat)) else Some (a/b) @@ -169,10 +163,10 @@ let div = ((a, b) : (nat, nat)) : option (nat) => *Pattern matching* is similiar to the `switch` construct in Javascript, and can be used to route the program's control flow based on the value of a variant. Consider for example the definition of a -function `flip` that flips a coin, as defined above. +function `flip` that flips a coin. - + ```pascaligo group=e type coin is Head | Tail diff --git a/gitlab-pages/docs/language-basics/variables-and-constants.md b/gitlab-pages/docs/language-basics/variables-and-constants.md index 87f2ab109..5c3c4ede2 100644 --- a/gitlab-pages/docs/language-basics/variables-and-constants.md +++ b/gitlab-pages/docs/language-basics/variables-and-constants.md @@ -13,7 +13,7 @@ declaration. When defining a constant you need to provide a `name`, `type` and a `value`: - + ```pascaligo group=a const age : int = 25 ``` @@ -53,14 +53,11 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constant ## Variables - + -Variables, unlike constants, are mutable. They cannot be declared in a -*global scope*, but they can be declared and used within functions, or -as function parameters. - -> 💡 Do not worry if you do not understand the function syntax yet. We -> will get to it in upcoming sections of this documentation. +Variables, unlike constants, are *mutable*. They cannot be declared in +a *global scope*, but they can be declared and used within functions, +or as function parameters. > ⚠️ Please be wary that mutation only works within the function scope > itself, values outside of the function scope will not be affected. @@ -68,7 +65,7 @@ as function parameters. ```pascaligo group=b // The following is invalid: use `const` for global values instead. -// var four : int = 4 +// var four : int := 4 function add (const a : int; const b : int) : int is block { @@ -90,11 +87,8 @@ ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/ As expected in the pure subset of a functional language, CameLIGO only -features constant values: once they are declared, the value cannot be -changed (or "mutated"). - -> 💡 Do not worry if you do not understand the function syntax yet. We -> will get to it in upcoming sections of this documentation. +features *constant values*: once they are declared, the value cannot +be changed (or "mutated"). ```cameligo group=c let add (a : int) (b : int) : int = @@ -110,12 +104,9 @@ ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/ As expected in the pure subset of a functional language, ReasonLIGO -only features constant values: once they are declared, the value +only features *constant values*: once they are declared, the value cannot be changed (or "mutated"). -> 💡 Do not worry if you do not understand the function syntax yet. We -> will get to it in upcoming sections of this documentation. - ```reasonligo group=c let add = ((a, b): (int, int)): int => { let c : int = a + b; diff --git a/gitlab-pages/website/core/CodeExamples.js b/gitlab-pages/website/core/CodeExamples.js index 4ace23b4e..9cdf27c1c 100644 --- a/gitlab-pages/website/core/CodeExamples.js +++ b/gitlab-pages/website/core/CodeExamples.js @@ -3,68 +3,83 @@ const React = require('react'); const pre = '```'; const PASCALIGO_EXAMPLE = `${pre}pascaligo -// variant defining pseudo multi-entrypoint actions -type action is -| Increment of int +type storage is int + +type parameter is + Increment of int | Decrement of int +| Reset -function add (const a : int ; const b : int) : int is a + b +type return is list (operation) * storage -function subtract (const a : int ; const b : int) : int is a - b +// Two entrypoints -// real entrypoint that re-routes the flow based on the action provided -function main (const p : action ; const s : int) : (list(operation) * int) is - ((nil : list(operation)), - case p of - | Increment (n) -> add (s, n) - | Decrement (n) -> subtract (s, n) - end) +function add (const store : storage; const delta : int) : storage is store + delta +function sub (const store : storage; const delta : int) : storage is store - delta + +(* Main access point that dispatches to the entrypoints according to + the smart contract parameter. *) + +function main (const action : parameter; const store : storage) : return is + ((nil : list (operation)), // No operations + case action of + Increment (n) -> add (store, n) + | Decrement (n) -> sub (store, n) + | Reset -> 0 + end) ${pre}`; const CAMELIGO_EXAMPLE = `${pre}ocaml type storage = int -(* variant defining pseudo multi-entrypoint actions *) - -type action = -| Increment of int +type parameter = + Increment of int | Decrement of int +| Reset -let add (a,b: int * int) : int = a + b -let sub (a,b: int * int) : int = a - b +type return = operation list * storage -(* real entrypoint that re-routes the flow based on the action provided *) +// Two entrypoints -let main (p,s: action * storage) = - let storage = - match p with - | Increment n -> add (s, n) - | Decrement n -> sub (s, n) - in ([] : operation list), storage +let add (store, delta : storage * int) : storage = store + delta +let sub (store, delta : storage * int) : storage = store - delta + +(* Main access point that dispatches to the entrypoints according to + the smart contract parameter. *) + +let main (action, store : parameter * storage) : return = + ([] : operation list), // No operations + (match action with + Increment (n) -> add (store, n) + | Decrement (n) -> sub (store, n) + | Reset -> 0) ${pre}`; const REASONLIGO_EXAMPLE = `${pre}reasonligo type storage = int; -/* variant defining pseudo multi-entrypoint actions */ +type parameter = + Increment (int) +| Decrement (int) +| Reset; -type action = - | Increment(int) - | Decrement(int); +type return = (list (operation), storage); -let add = ((a,b): (int, int)): int => a + b; -let sub = ((a,b): (int, int)): int => a - b; +(* Two entrypoints *) -/* real entrypoint that re-routes the flow based on the action provided */ +let add = ((store, delta) : (storage, int)) : storage => store + delta; +let sub = ((store, delta) : (storage, int)) : storage => store - delta; -let main = ((p,storage): (action, storage)) => { - let storage = - switch (p) { - | Increment(n) => add((storage, n)) - | Decrement(n) => sub((storage, n)) - }; - ([]: list(operation), storage); +(* Main access point that dispatches to the entrypoints according to + the smart contract parameter. *) + +let main = ((action, store) : (parameter, storage)) : return => { + (([] : list (operation)), // No operations + (switch (action) { + | Increment (n) => add ((store, n)) + | Decrement (n) => sub ((store, n)) + | Reset => 0})) }; ${pre}`; diff --git a/gitlab-pages/website/pages/en/index.js b/gitlab-pages/website/pages/en/index.js index a9bc41277..9bbddcb17 100644 --- a/gitlab-pages/website/pages/en/index.js +++ b/gitlab-pages/website/pages/en/index.js @@ -6,14 +6,14 @@ const docUrl = require(`${process.cwd()}/core/UrlUtils`).docUrl; const FEATURES = [ { image: 'img/strong-type-system.svg', - title: 'Strong Type System', + title: 'Strong, Static Type System', content: 'Write types, then code. Benefit from the safety of type systems.' }, { image: 'img/syntax-agnostic.svg', - title: 'Syntax Agnostic', + title: 'Polyglot', content: - 'Code in your language. Write PascaLIGO, CameLIGO, or add your own syntax.' + 'Code in your language. Write PascaLIGO, CameLIGO, ReasonLIGO or add your own syntax.' }, { image: 'img/easy-integration.svg', @@ -77,7 +77,7 @@ module.exports = props => {
-

A friendly smart-contract language for Tezos

+

A friendly Smart Contract Language for Tezos

Michelson was never so easy

diff --git a/gitlab-pages/website/siteConfig.js b/gitlab-pages/website/siteConfig.js index c42f07a31..b0f7d419c 100644 --- a/gitlab-pages/website/siteConfig.js +++ b/gitlab-pages/website/siteConfig.js @@ -4,7 +4,7 @@ let reasonHighlightJs = require('reason-highlightjs'); const siteConfig = { title: 'LIGO', // Title for your website. - tagline: 'LIGO is a friendly smart-contract language for Tezos', + tagline: 'LIGO, the friendly Smart Contract Language for Tezos', taglineSub: 'Michelson was never so easy', url: 'https://ligolang.org', // Your website URL baseUrl: '/', // Base URL for your project */ @@ -14,7 +14,7 @@ const siteConfig = { // Used for publishing and more projectName: 'ligo', - organizationName: 'marigold', + organizationName: 'TBN', // For top-level user or org sites, the organization is still the same. // e.g., for the https://JoelMarcey.github.io site, it would be set like... // organizationName: 'JoelMarcey' @@ -87,10 +87,11 @@ const siteConfig = { beginKeywords: '', keywords: { keyword: - 'and begin block case const contains down else end fail for ' + - 'from function if in is list map mod nil not of or patch ' + - 'procedure record remove set skip step then to type var while with', - literal: 'true false unit int string some none bool nat list' + 'and attributes begin big_map block case const contains else' + + ' end False for from function if in is list map mod nil' + + ' not of or patch record remove set skip then to True type' + + ' var while with', + literal: 'true false unit int string Some None bool nat list' }, lexemes: '[a-zA-Z][a-zA-Z0-9_]*', contains: [ diff --git a/gitlab-pages/website/versioned_docs/version-next/contributors/origin.md b/gitlab-pages/website/versioned_docs/version-next/contributors/origin.md index 3b3820ef0..805f45c40 100644 --- a/gitlab-pages/website/versioned_docs/version-next/contributors/origin.md +++ b/gitlab-pages/website/versioned_docs/version-next/contributors/origin.md @@ -4,8 +4,8 @@ title: Origin original_id: origin --- -LIGO is a programming language that aims to provide developers with an uncomplicated and safer way to implement smart-contracts. LIGO is currently being implemented for the Tezos blockchain and as a result, it compiles down to Michelson - the native smart-contract language of Tezos. +LIGO is a programming language that aims to provide developers with an uncomplicated and safe way to implement smart-contracts. Since it is being implemented for the Tezos blockchain LIGO compiles to Michelson—the native smart-contract language of Tezos. > Smart-contracts are programs that run within a blockchain network. -LIGO was initially meant to be a language for developing Marigold, on top of a hacky framework called Meta-Michelson. However, due to the attention received by the Tezos community, a decision has been put into action to develop LIGO as a standalone language that will support Tezos directly as well. \ No newline at end of file +LIGO was meant to be a language for developing Marigold on top of a hacky framework called Meta-Michelson. However, due to the attention received by the Tezos community, LIGO is now a standalone language being developed to support Tezos directly. \ No newline at end of file diff --git a/gitlab-pages/website/versioned_docs/version-next/contributors/philosophy.md b/gitlab-pages/website/versioned_docs/version-next/contributors/philosophy.md index 349c6d131..1d2dc2e3c 100644 --- a/gitlab-pages/website/versioned_docs/version-next/contributors/philosophy.md +++ b/gitlab-pages/website/versioned_docs/version-next/contributors/philosophy.md @@ -4,23 +4,22 @@ title: Philosophy original_id: philosophy --- -To understand LIGO’s design choices, it’s important to get its philosophy. There are two main concerns that we have in mind when building LIGO. - - +To understand LIGO’s design choices it’s important to understand its philosophy. We have two main concerns in mind while building LIGO. ## Safety Once a smart-contract is deployed, it will likely be impossible to change it. You must get it right on the first try, and LIGO should help as much as possible. There are multiple ways to make LIGO a safer language for smart-contracts. ### Automated Testing -Automated Testing is the process through which a program will run some other program, and check that this other program behaves correctly. +Automated Testing is the process through which a program runs another program, and checks that this other program behaves correctly. + There already is a testing library for LIGO programs written in OCaml that is used to test LIGO itself. Making it accessible to users will greatly improve safety. A way to do so would be to make it accessible from within LIGO. ### Static Analysis Static analysis is the process of having a program analyze another one. -For instance, type systems are a kind of static analysis through which it is possible to find lots of bugs. There is already a fairly simple type system in LIGO, and we plan to make it much stronger. +For instance, type systems are a kind of static analysis through which it is possible to find lots of bugs. LIGO already has a simple type system, and we plan to make it much stronger. ### Conciseness -Writing less code gives you less room to introduce errors and that's why LIGO encourages writing lean rather than chunky smart-contracts. +Writing less code gives you less room to introduce errors. That's why LIGO encourages writing lean rather than chunky smart-contracts. --- diff --git a/gitlab-pages/website/versioned_sidebars/version-next-sidebars.json b/gitlab-pages/website/versioned_sidebars/version-next-sidebars.json index e74ff6d36..07fc287dd 100644 --- a/gitlab-pages/website/versioned_sidebars/version-next-sidebars.json +++ b/gitlab-pages/website/versioned_sidebars/version-next-sidebars.json @@ -6,15 +6,27 @@ "version-next-intro/editor-support" ], "Language Basics": [ - "version-next-language-basics/cheat-sheet", "version-next-language-basics/types", - "version-next-language-basics/variables", + "version-next-language-basics/constants-and-variables", + "version-next-language-basics/math-numbers-tez", + "version-next-language-basics/strings", "version-next-language-basics/functions", - "version-next-language-basics/entrypoints", - "version-next-language-basics/operators" + "version-next-language-basics/boolean-if-else", + "version-next-language-basics/loops", + "version-next-language-basics/unit-option-pattern-matching", + "version-next-language-basics/maps-records", + "version-next-language-basics/sets-lists-tuples", + "version-next-language-basics/tezos-specific" + ], + "Advanced": [ + "version-next-advanced/timestamps-addresses", + "version-next-advanced/entrypoints-contracts", + "version-next-advanced/include", + "version-next-advanced/first-contract" ], "API": [ - "version-next-api-cli-commands" + "version-next-api/cli-commands", + "version-next-api/cheat-sheet" ] }, "version-next-contributors-docs": { diff --git a/src/passes/operators/operators.ml b/src/passes/operators/operators.ml index be1196ad1..42aa97936 100644 --- a/src/passes/operators/operators.ml +++ b/src/passes/operators/operators.ml @@ -32,7 +32,7 @@ module Simplify = struct - The left-hand-side is the reserved name in the given front-end. - The right-hand-side is the name that will be used in the AST. *) - let unit_expr = make_t @@ T_constant TC_unit + let unit_expr = make_t @@ T_constant TC_unit let type_constants s = match s with @@ -185,6 +185,7 @@ module Simplify = struct | "Set.literal" -> ok C_SET_LITERAL | "Set.add" -> ok C_SET_ADD | "Set.remove" -> ok C_SET_REMOVE + | "Set.iter" -> ok C_SET_ITER | "Set.fold" -> ok C_SET_FOLD | "Set.size" -> ok C_SIZE @@ -1152,7 +1153,7 @@ module Compiler = struct | C_CONCAT -> ok @@ simple_binary @@ prim I_CONCAT | C_CHAIN_ID -> ok @@ simple_constant @@ prim I_CHAIN_ID | _ -> simple_fail @@ Format.asprintf "operator not implemented for %a" Stage_common.PP.constant c - + (* Some complex operators will need to be added in compiler/compiler_program.