More documentation rewrites.
This commit is contained in:
parent
e6dc4371ee
commit
82aacde97f
@ -1,20 +1,20 @@
|
|||||||
---
|
---
|
||||||
id: entrypoints-contracts
|
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
|
A LIGO contract is made of a series of constant and function
|
||||||
declarations. Only functions having a special type can be called when
|
declarations. Only functions having a special type can be called when
|
||||||
the contract is activated: they are called *entrypoints*. An
|
the contract is activated: we called them *access functions*. An
|
||||||
entrypoint need to take two parameters, the *contract parameter* and
|
access function takes two parameters, the *contract parameter* and the
|
||||||
the *on-chain storage*, and return a pair made of a *list of
|
*on-chain storage*, and returns a pair made of a *list of operations*
|
||||||
operations* and a (new) storage.
|
and a (new) storage.
|
||||||
|
|
||||||
When the contract is originated, the initial value of the storage is
|
When the contract is originated, the initial value of the storage is
|
||||||
provided. When and entrypoint is later called, only the parameter is
|
provided. When an access function is later called, only the parameter
|
||||||
provided, but the type of an entrypoint contains both.
|
is provided, but the type of an access function contains both.
|
||||||
|
|
||||||
The type of the contract parameter and the storage are up to the
|
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
|
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.)
|
name for the storage.)
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo skip
|
```pascaligo skip
|
||||||
type storage is ... // Any name, any type
|
type storage is ... // Any name, any type
|
||||||
type return is list (operation) * storage
|
type return is list (operation) * storage
|
||||||
@ -42,9 +42,9 @@ type return = (list (operation), storage);
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
The contract storage can only be modified by activating an
|
The contract storage can only be modified by activating an access
|
||||||
entrypoint. It is important to understand what that means. What it
|
function. It is important to understand what that means. What it does
|
||||||
does *not* mean is that some global variable holding the storage is
|
*not* mean is that some global variable holding the storage is
|
||||||
modified by the entrypoint. Instead, what it *does* mean is that,
|
modified by the entrypoint. Instead, what it *does* mean is that,
|
||||||
given the state of the storage *on-chain*, an entrypoint specifies how
|
given the state of the storage *on-chain*, an entrypoint specifies how
|
||||||
to create another state for it, depending on a parameter.
|
to create another state for it, depending on a parameter.
|
||||||
@ -54,7 +54,7 @@ is updated by the parameter.
|
|||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
type storage is nat
|
type storage is nat
|
||||||
@ -82,20 +82,29 @@ let main = ((parameter, store): (nat, storage)) : return => {
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
In LIGO, the design pattern for entrypoints consists in actually
|
## Entrypoints
|
||||||
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.
|
|
||||||
|
|
||||||
In the following example, the storage contains a counter (of type
|
In LIGO, the design pattern is to have *one* access function that
|
||||||
`nat`) and a name (of type `string`). Depending on the parameter of
|
dispatches the control flow according to its parameter. Those
|
||||||
the contract, either the counter or the name is updated.
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
type parameter is
|
type parameter is
|
||||||
Entrypoint_A of nat
|
Entrypoint_A of nat
|
||||||
@ -188,7 +197,7 @@ any transaction that sends more tez than `0mutez`, that is, no
|
|||||||
incoming tokens are accepted.
|
incoming tokens are accepted.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
type parameter is unit
|
type parameter is unit
|
||||||
type storage 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.
|
This example shows how `sender` or `source` can be used to deny access to an entrypoint.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
||||||
|
|
||||||
@ -287,7 +296,7 @@ counter contract.
|
|||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo skip
|
```pascaligo skip
|
||||||
// counter.ligo
|
// counter.ligo
|
||||||
type parameter is
|
type parameter is
|
||||||
|
@ -3,20 +3,24 @@ id: first-contract
|
|||||||
title: 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
|
- `file` - contract to run
|
||||||
- `entrypoint` - name of the function to execute
|
- `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
|
- `storage` - a mock storage value, as if it were stored on a real chain
|
||||||
|
|
||||||
Here's a full example:
|
Here is a full example:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
@ -29,25 +33,29 @@ ligo dry-run src/basic.ligo main Unit Unit
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
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`.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```
|
```
|
||||||
type action is
|
type action is
|
||||||
| Increment of int
|
Increment of int
|
||||||
| Decrement of int
|
| Decrement of int
|
||||||
|
|
||||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
function main (const p : action ; const s : int) : (list(operation) * int) is
|
||||||
block {skip} with ((nil : list(operation)),
|
((nil : list(operation)),
|
||||||
case p of
|
(case p of
|
||||||
| Increment (n) -> s + n
|
| Increment (n) -> s + n
|
||||||
| Decrement (n) -> s - n
|
| Decrement (n) -> s - n
|
||||||
end)
|
end))
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
@ -98,11 +106,13 @@ ligo dry-run src/counter.ligo main "Increment(5)" 5
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
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
|
## 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:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
@ -156,7 +166,10 @@ Command above will output the following Michelson code:
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
@ -182,4 +195,5 @@ ligo compile-parameter src/counter.ligo main 'Increment(5)'
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
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`
|
||||||
|
@ -16,7 +16,7 @@ expression, please be aware that it is up to the baker to set the
|
|||||||
current timestamp value.
|
current timestamp value.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const today : timestamp = now
|
const today : timestamp = now
|
||||||
```
|
```
|
||||||
@ -44,7 +44,7 @@ constraints on your smart contracts. Consider the following scenarios.
|
|||||||
#### In 24 hours
|
#### In 24 hours
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
const today : timestamp = now
|
const today : timestamp = now
|
||||||
const one_day : int = 86400
|
const one_day : int = 86400
|
||||||
@ -76,7 +76,7 @@ let one_day_later : timestamp = some_date + one_day;
|
|||||||
#### 24 hours Ago
|
#### 24 hours Ago
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const today : timestamp = now
|
const today : timestamp = now
|
||||||
const one_day : int = 86400
|
const one_day : int = 86400
|
||||||
@ -105,7 +105,7 @@ You can compare timestamps using the same comparison operators
|
|||||||
applying to numbers.
|
applying to numbers.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const not_tommorow : bool = (now = in_24_hrs)
|
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.
|
the following examples.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=d
|
```pascaligo group=d
|
||||||
const my_account : address =
|
const my_account : address =
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)
|
||||||
@ -159,7 +159,7 @@ failure if the signature is invalid.
|
|||||||
Here is how you can define a signature:
|
Here is how you can define a signature:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=e
|
```pascaligo group=e
|
||||||
const my_sig : signature =
|
const my_sig : signature =
|
||||||
("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" :
|
("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" :
|
||||||
@ -188,7 +188,7 @@ failure if the key is invalid.
|
|||||||
Here is how you can define a key.
|
Here is how you can define a key.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=f
|
```pascaligo group=f
|
||||||
const my_key : key =
|
const my_key : key =
|
||||||
("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key)
|
("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key)
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
type action is
|
type storage is int
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
|
||||||
| Reset of unit
|
|
||||||
|
|
||||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
type parameter is
|
||||||
block { skip } with ((nil : list(operation)),
|
Increment of int
|
||||||
case p of
|
| Decrement of int
|
||||||
| Increment(n) -> s + n
|
| Reset
|
||||||
| Decrement(n) -> s - n
|
|
||||||
| Reset(n) -> 0
|
type return is list (operation) * storage
|
||||||
end)
|
|
||||||
|
(* 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)
|
||||||
|
23
gitlab-pages/docs/intro/src/what-and-why/ligo-counter.mligo
Normal file
23
gitlab-pages/docs/intro/src/what-and-why/ligo-counter.mligo
Normal file
@ -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)
|
26
gitlab-pages/docs/intro/src/what-and-why/ligo-counter.religo
Normal file
26
gitlab-pages/docs/intro/src/what-and-why/ligo-counter.religo
Normal file
@ -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
|
||||||
|
}))
|
||||||
|
};
|
@ -1,14 +1,73 @@
|
|||||||
---
|
---
|
||||||
id: what-and-why
|
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`**
|
**`counter.tz`**
|
||||||
```text
|
```text
|
||||||
@ -21,111 +80,160 @@ Here's an example of Michelson code:
|
|||||||
NIL operation ; PAIR } }
|
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
|
Michelson code consists of *instructions* like `IF_LEFT`, `PUSH ...`,
|
||||||
- `storage` - Type definition for the contract's data storage.
|
`UNPAIR` etc. that are composed sequentially in what is called a
|
||||||
- `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.
|
*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
|
Perhaps the biggest challenge when programming in Michelson is the
|
||||||
|
lack of *variables* to denote the data: the stack layout has to be
|
||||||
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:
|
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`**
|
**`counter.js`**
|
||||||
```javascript
|
```javascript
|
||||||
var storage = 0;
|
var storage = 0;
|
||||||
|
|
||||||
function add(a) {
|
function add (a) { storage += a; }
|
||||||
storage += a
|
function sub (a) { storage -= a; }
|
||||||
}
|
|
||||||
|
|
||||||
function sub(a) {
|
// We are calling this function "reset" instead of "default"
|
||||||
storage -= a
|
// because `default` is a Javascript keyword
|
||||||
}
|
|
||||||
|
|
||||||
// We're calling this function reset instead of default
|
function reset () { storage = 0; }
|
||||||
// because `default` is a javascript keyword
|
|
||||||
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
type action is
|
type storage is int
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
|
||||||
| Reset of unit
|
|
||||||
|
|
||||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
type parameter is
|
||||||
block { skip } with ((nil : list(operation)),
|
Increment of int
|
||||||
case p of
|
| Decrement of int
|
||||||
| Increment(n) -> s + n
|
| Reset
|
||||||
| Decrement(n) -> s - n
|
|
||||||
| Reset(n) -> 0
|
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)
|
end)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=a
|
||||||
type action =
|
type storage = int
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
|
||||||
| Reset of unit
|
|
||||||
|
|
||||||
let main (p, s: action * int) : operation list * int =
|
type parameter =
|
||||||
let result =
|
Increment of int
|
||||||
match p with
|
| Decrement of int
|
||||||
| Increment n -> s + n
|
| Reset
|
||||||
| Decrement n -> s - n
|
|
||||||
| Reset n -> 0
|
type return = operation list * storage
|
||||||
in
|
|
||||||
(([]: operation list), result)
|
let main (action, store : parameter * storage) : return =
|
||||||
|
([] : operation list),
|
||||||
|
(match action with
|
||||||
|
Increment n -> store + n
|
||||||
|
| Decrement n -> store - n
|
||||||
|
| Reset -> 0)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=a
|
||||||
type action =
|
type storage = int;
|
||||||
| Increment(int)
|
|
||||||
| Decrement(int)
|
|
||||||
| Reset(unit);
|
|
||||||
|
|
||||||
let main = ((p,s): (action, int)) : (list(operation), int) => {
|
type parameter =
|
||||||
let result =
|
Increment (int)
|
||||||
switch (p) {
|
| Decrement (int)
|
||||||
| Increment(n) => s + n
|
| Reset;
|
||||||
| Decrement(n) => s - n
|
|
||||||
| Reset n => 0
|
type return = (list (operation), storage);
|
||||||
};
|
|
||||||
(([]: list(operation)), result);
|
let main = ((action, store): (parameter, storage)) : return => {
|
||||||
|
(([] : list (operation)),
|
||||||
|
(switch (action) {
|
||||||
|
| Increment (n) => store + n
|
||||||
|
| Decrement (n) => store - n
|
||||||
|
| Reset => 0}));
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
> 💡 You can find the Michelson compilation output of the contract -->
|
||||||
|
<!--above in **`ligo-counter.tz`** -->
|
||||||
|
|
||||||
> 💡 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.
|
**The Michelson contract also checks if the `AMOUNT` sent is `0`*
|
||||||
|
|
||||||
**not exactly, the Michelson contract also checks if the `AMOUNT` sent is `0`*
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ The type of a boolean value is `bool`. Here is how to define a boolean
|
|||||||
value:
|
value:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const a : bool = True // Notice the capital letter
|
const a : bool = True // Notice the capital letter
|
||||||
const b : bool = False // Same.
|
const b : bool = False // Same.
|
||||||
@ -19,7 +19,6 @@ const b : bool = False // Same.
|
|||||||
let a : bool = true
|
let a : bool = true
|
||||||
let b : bool = false
|
let b : bool = false
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
let a : bool = true;
|
let a : bool = true;
|
||||||
@ -27,8 +26,7 @@ let b : bool = false;
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Comparing Values
|
||||||
## Comparing two Values
|
|
||||||
|
|
||||||
In LIGO, only values of the same type can be compared. Moreover, not
|
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
|
all values of the same type can be compared, only those with
|
||||||
@ -42,7 +40,7 @@ function.
|
|||||||
### Comparing Strings
|
### Comparing Strings
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
const a : string = "Alice"
|
const a : string = "Alice"
|
||||||
const b : string = "Alice"
|
const b : string = "Alice"
|
||||||
@ -62,11 +60,10 @@ let c : bool = (a == b); // true
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### Comparing numbers
|
### Comparing numbers
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const a : int = 5
|
const a : int = 5
|
||||||
const b : int = 4
|
const b : int = 4
|
||||||
@ -102,14 +99,13 @@ let h : bool = (a != b);
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### Comparing tez
|
### Comparing tez
|
||||||
|
|
||||||
> 💡 Comparing `tez` values is especially useful when dealing with an
|
> 💡 Comparing `tez` values is especially useful when dealing with an
|
||||||
> amount sent in a transaction.
|
> amount sent in a transaction.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=d
|
```pascaligo group=d
|
||||||
const a : tez = 5mutez
|
const a : tez = 5mutez
|
||||||
const b : tez = 10mutez
|
const b : tez = 10mutez
|
||||||
@ -136,7 +132,7 @@ Conditional logic enables forking the control flow depending on the
|
|||||||
state.
|
state.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=e
|
```pascaligo group=e
|
||||||
type magnitude is Small | Large // See variant types.
|
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'
|
gitlab-pages/docs/language-basics/boolean-if-else/cond.religo compare 21n'
|
||||||
# Outputs: Large
|
# Outputs: Large
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,47 +3,42 @@ id: functions
|
|||||||
title: Functions
|
title: Functions
|
||||||
---
|
---
|
||||||
|
|
||||||
Writing code is fun as long as it does not get out of hand. To make
|
LIGO features functions are the basic building block of contracts. For
|
||||||
sure our code does not turn into spaghetti, we can structure some
|
example, entrypoints are functions.
|
||||||
logic into functions.
|
|
||||||
|
|
||||||
## Blocks
|
## Declaring Functions
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
There are two ways in PascaLIGO to define functions: with or without a
|
||||||
|
*block*.
|
||||||
|
|
||||||
|
### Blocks
|
||||||
|
|
||||||
In PascaLIGO, *blocks* enable the sequential composition of
|
In PascaLIGO, *blocks* enable the sequential composition of
|
||||||
instructions into an isolated scope. Each block needs to include at
|
instructions into an isolated scope. Each block needs to include at
|
||||||
least one instruction. If we need a placeholder, we use the
|
least one instruction.
|
||||||
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.
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
|
|
||||||
```pascaligo skip
|
```pascaligo skip
|
||||||
// terse style
|
|
||||||
block { a := a + 1 }
|
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
|
```pascaligo skip
|
||||||
// terse style
|
block { skip }
|
||||||
block { const a : int = 1 }
|
|
||||||
// verbose style
|
|
||||||
begin
|
|
||||||
const a : int = 1
|
|
||||||
end
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
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 }
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```
|
||||||
<!--Pascaligo-->
|
|
||||||
|
|
||||||
Functions in PascaLIGO are defined using the `function` keyword
|
Functions in PascaLIGO are defined using the `function` keyword
|
||||||
followed by their `name`, `parameters` and `return` type definitions.
|
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:
|
The function body consists of two parts:
|
||||||
|
|
||||||
- `block { <instructions and declarations> }` - logic of the function
|
- `block { <instructions and declarations> }` is the logic of the function;
|
||||||
- `with <value>` - the value returned by the function
|
- `with <value>` 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
|
```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
|
||||||
```
|
```
|
||||||
@ -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.
|
The function body is a single expression, whose value is returned.
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO--> Functions in ReasonLIGO are defined using the `let`
|
||||||
Functions in ReasonLIGO are defined using the `let` keyword, like
|
keyword, like other values. The difference is that a tuple of
|
||||||
other values. The difference is that a succession of parameters is
|
parameters is provided after the value name, with its type, then
|
||||||
provided after the value name, followed by the return type.
|
followed by the return type.
|
||||||
|
|
||||||
Here is how you define a basic function that sums two integers:
|
Here is how you define a basic function that sums two integers:
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
@ -163,7 +166,7 @@ a key in a record or a map.
|
|||||||
Here is how to define an anonymous function:
|
Here is how to define an anonymous function:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
function increment (const b : int) : int is
|
function increment (const b : int) : int is
|
||||||
(function (const a : int) : int is a + 1) (b)
|
(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-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let increment = (b : int) : int => ((a : int) : int => a + 1)(b);
|
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
|
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.
|
function to all its elements.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
function incr_map (const l : list (int)) : list (int) is
|
function incr_map (const l : list (int)) : list (int) is
|
||||||
list_map (function (const i : int) : int is i + 1, l)
|
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 ]
|
# Outputs: [ 2 ; 3 ; 4 ]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let incr_map (l : int list) : int list =
|
let incr_map (l : int list) : int list =
|
||||||
List.map (fun (i : int) -> i + 1) l
|
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 ]
|
# Outputs: [ 2 ; 3 ; 4 ]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let incr_map = (l : list (int)) : list (int) =>
|
let incr_map = (l : list (int)) : list (int) =>
|
||||||
List.map ((i : int) => i + 1, l);
|
List.map ((i : int) => i + 1, l);
|
||||||
|
@ -7,36 +7,37 @@ title: Loops
|
|||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
General iteration in PascaLIGO takes the shape of "while" loops, which
|
General iteration in PascaLIGO takes the shape of general loops, which
|
||||||
should be familiar to programmers of imperative languages. Those loops
|
should be familiar to programmers of imperative languages as "while
|
||||||
are of the form `while <condition> <block>`. Their associated block is
|
loops". Those loops are of the form `while <condition> <block>`. Their
|
||||||
repeatedly evaluated until the condition becomes true, or never
|
associated block is repeatedly evaluated until the condition becomes
|
||||||
evaluated if the condition is false at the start. The loop never
|
true, or never evaluated if the condition is false at the start. The
|
||||||
terminates if the condition never becomes true. Because we are writing
|
loop never terminates if the condition never becomes true. Because we
|
||||||
smart contracts on Tezos, when the condition of a "while" loops fails
|
are writing smart contracts on Tezos, when the condition of a "while"
|
||||||
to become true, the execution will run out of gas and stop with a
|
loops fails to become true, the execution will run out of gas and stop
|
||||||
failure anyway.
|
with a failure anyway.
|
||||||
|
|
||||||
Here is how to compute the greatest common divisors of two natural
|
Here is how to compute the greatest common divisors of two natural
|
||||||
number by means of Euclid's algorithm:
|
number by means of Euclid's algorithm:
|
||||||
|
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
function gcd (var x : nat; var y : nat) : nat is block {
|
function gcd (var x : nat; var y : nat) : nat is
|
||||||
if x < y then
|
block {
|
||||||
block {
|
if x < y then
|
||||||
const z : nat = x;
|
block {
|
||||||
x := y; y := z
|
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;
|
} with x
|
||||||
var r : nat := 0n;
|
|
||||||
while y =/= 0n block {
|
|
||||||
r := x mod y;
|
|
||||||
x := y;
|
|
||||||
y := r
|
|
||||||
}
|
|
||||||
} with x
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can call the function `gcd` defined above using the LIGO compiler
|
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
|
must have the type `bool * t` (not simply `t`). It is the boolean
|
||||||
value that denotes whether the stopping condition has been reached.
|
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
|
```cameligo group=a
|
||||||
let iter (x,y : nat * nat) : bool * (nat * nat) =
|
let iter (x,y : nat * nat) : bool * (nat * nat) =
|
||||||
if y = 0n then false, (x,y) else true, (y, x mod y)
|
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)
|
let x,y = Loop.fold_while iter (x,y)
|
||||||
in x
|
in x
|
||||||
```
|
```
|
||||||
|
|
||||||
You can call the function `gcd` defined above using the LIGO compiler
|
You can call the function `gcd` defined above using the LIGO compiler
|
||||||
like so:
|
like so:
|
||||||
```shell
|
```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
|
`t`). It is the boolean value that denotes whether the stopping
|
||||||
condition has been reached.
|
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
|
```reasonligo group=a
|
||||||
let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) =>
|
let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) =>
|
||||||
if (y == 0n) { (false, (x,y)); } else { (true, (y, x mod y)); };
|
if (y == 0n) { (false, (x,y)); } else { (true, (y, x mod y)); };
|
||||||
@ -139,15 +145,15 @@ let gcd = ((x,y) : (nat, nat)) : nat => {
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## For Loops
|
## Bounded Loops
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
In addition to general loops, PascaLIGO features a specialised kind of
|
||||||
<!--Pascaligo-->
|
*loop to iterate over bounded intervals*. These loops are familiarly
|
||||||
|
known as "for loops" and they have the form `for <variable assignment> to
|
||||||
|
<upper bound> <block>`, which is familiar for programmers of
|
||||||
|
imperative languages.
|
||||||
|
|
||||||
To iterate over a range of integers you use a loop of the form `for
|
Consider how to sum integers from `0` to `n`:
|
||||||
<variable assignment> to <upper integer bound> <block>`, which is
|
|
||||||
familiar for programmers of imperative languages. Note that, for the
|
|
||||||
sake of generality, the bounds are of type `int`, not `nat`.
|
|
||||||
|
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
function sum (var n : nat) : int is block {
|
function sum (var n : nat) : int is block {
|
||||||
@ -158,6 +164,8 @@ function sum (var n : nat) : int is block {
|
|||||||
} with acc
|
} 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
|
You can call the function `sum` defined above using the LIGO compiler
|
||||||
like so:
|
like so:
|
||||||
```shell
|
```shell
|
||||||
@ -165,11 +173,6 @@ ligo run-function
|
|||||||
gitlab-pages/docs/language-basics/src/loops/sum.ligo sum 7n
|
gitlab-pages/docs/language-basics/src/loops/sum.ligo sum 7n
|
||||||
# Outputs: 28
|
# Outputs: 28
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
<!--Pascaligo-->
|
|
||||||
|
|
||||||
PascaLIGO "for" loops can also iterate through the contents of a
|
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
|
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]'
|
'map ["1"->1; "2"->2; "3"->3]'
|
||||||
# Outputs: ( "123", 6 )
|
# Outputs: ( "123", 6 )
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
@ -17,7 +17,8 @@ special operator (`.`).
|
|||||||
Let us first consider and example of record type declaration.
|
Let us first consider and example of record type declaration.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
type user is
|
type user is
|
||||||
record [
|
record [
|
||||||
@ -27,7 +28,7 @@ type user is
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
type user = {
|
type user = {
|
||||||
id : nat;
|
id : nat;
|
||||||
@ -36,7 +37,7 @@ type user = {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
type user = {
|
type user = {
|
||||||
id : nat,
|
id : nat,
|
||||||
@ -49,7 +50,7 @@ type user = {
|
|||||||
And here is how a record value is defined:
|
And here is how a record value is defined:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const alice : user =
|
const alice : user =
|
||||||
record [
|
record [
|
||||||
@ -59,7 +60,7 @@ const alice : user =
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
let alice : user = {
|
let alice : user = {
|
||||||
id = 1n;
|
id = 1n;
|
||||||
@ -68,7 +69,7 @@ let alice : user = {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
let alice : user = {
|
let alice : user = {
|
||||||
id : 1n,
|
id : 1n,
|
||||||
@ -80,21 +81,21 @@ let alice : user = {
|
|||||||
|
|
||||||
### Accessing Record Fields
|
### 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:
|
operator, like so:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const alice_admin : bool = alice.is_admin
|
const alice_admin : bool = alice.is_admin
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
let alice_admin : bool = alice.is_admin
|
let alice_admin : bool = alice.is_admin
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
let alice_admin: bool = alice.is_admin;
|
let alice_admin: bool = alice.is_admin;
|
||||||
```
|
```
|
||||||
@ -118,7 +119,7 @@ points on a plane.
|
|||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
type point is record [x : int; y : int; z : int]
|
type point is record [x : int; y : int; z : int]
|
||||||
type vector is record [dx : int; dy : 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
|
update: a namless new version of it has been created and returned by
|
||||||
the blockless function.
|
the blockless function.
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
The syntax for the functional updates of record in CameLIGO follows
|
The syntax for the functional updates of record in CameLIGO follows
|
||||||
that of OCaml:
|
that of OCaml:
|
||||||
@ -156,7 +157,6 @@ let origin : point = {x = 0; y = 0; z = 0}
|
|||||||
let xy_translate (p, vec : point * vector) : point =
|
let xy_translate (p, vec : point * vector) : point =
|
||||||
{p with x = p.x + vec.dx; y = p.y + vec.dy}
|
{p with x = p.x + vec.dx; y = p.y + vec.dy}
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
You can call the function `xy_translate` defined above by running the
|
You can call the function `xy_translate` defined above by running the
|
||||||
following command of the shell:
|
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
|
> functional update: a nameless new version of it has been created and
|
||||||
> returned.
|
> returned.
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
The syntax for the functional updates of record in ReasonLIGO follows
|
The syntax for the functional updates of record in ReasonLIGO follows
|
||||||
that of OCaml:
|
that of OCaml:
|
||||||
@ -187,7 +187,7 @@ let xy_translate = ((p, vec) : (point, vector)) : point =>
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
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:
|
following command of the shell:
|
||||||
```shell
|
```shell
|
||||||
ligo run-function
|
ligo run-function
|
||||||
@ -213,9 +213,6 @@ name "patch").
|
|||||||
Let us consider defining a function that translates three-dimensional
|
Let us consider defining a function that translates three-dimensional
|
||||||
points on a plane.
|
points on a plane.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
type point is record [x : int; y : int; z : int]
|
type point is record [x : int; y : int; z : int]
|
||||||
type vector is record [dx : int; dy : 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]
|
const origin : point = record [x = 0; y = 0; z = 0]
|
||||||
|
|
||||||
function xy_translate (var p : point; const vec : vector) : point is block {
|
function xy_translate (var p : point; const vec : vector) : point is
|
||||||
const p : point = p with record [x = p.x + vec.dx; y = p.y + vec.dy]
|
block {
|
||||||
} with p
|
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
|
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`.
|
The hiding of a variable by another (here `p`) is called `shadowing`.
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
|
||||||
## Maps
|
## Maps
|
||||||
|
|
||||||
*Maps* are a data structure which associate values of the same type to
|
*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.
|
defined.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=f
|
```pascaligo group=f
|
||||||
type move is int * int
|
type move is int * int
|
||||||
type register is map (address, move)
|
type register is map (address, move)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=f
|
```cameligo group=f
|
||||||
type move = int * int
|
type move = int * int
|
||||||
type register = (address, move) map
|
type register = (address, move) map
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=f
|
```reasonligo group=f
|
||||||
type move = (int, int);
|
type move = (int, int);
|
||||||
type register = map (address, move);
|
type register = map (address, move);
|
||||||
@ -326,7 +321,7 @@ type register = map (address, move);
|
|||||||
And here is how a map value is defined:
|
And here is how a map value is defined:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
```pascaligo group=f
|
```pascaligo group=f
|
||||||
const moves : register =
|
const moves : register =
|
||||||
@ -340,7 +335,7 @@ const moves : register =
|
|||||||
> address)` means that we cast a string into an address. Also, `map`
|
> address)` means that we cast a string into an address. Also, `map`
|
||||||
> is a keyword.
|
> is a keyword.
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=f
|
```cameligo group=f
|
||||||
let moves : register =
|
let moves : register =
|
||||||
Map.literal [
|
Map.literal [
|
||||||
@ -353,7 +348,7 @@ let moves : register =
|
|||||||
> separate individual map entries. `("<string value>": address)`
|
> separate individual map entries. `("<string value>": address)`
|
||||||
> means that we type-cast a string into an address.
|
> means that we type-cast a string into an address.
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=f
|
```reasonligo group=f
|
||||||
let moves : register =
|
let moves : register =
|
||||||
Map.literal ([
|
Map.literal ([
|
||||||
@ -375,19 +370,19 @@ associated to a given key (`address` here) in the register. Here is an
|
|||||||
example:
|
example:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=f
|
```pascaligo group=f
|
||||||
const my_balance : option (move) =
|
const my_balance : option (move) =
|
||||||
moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)]
|
moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=f
|
```cameligo group=f
|
||||||
let my_balance : move option =
|
let my_balance : move option =
|
||||||
Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves
|
Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=f
|
```reasonligo group=f
|
||||||
let my_balance : option (move) =
|
let my_balance : option (move) =
|
||||||
Map.find_opt (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), moves);
|
Map.find_opt (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), moves);
|
||||||
@ -400,7 +395,7 @@ the reader to account for a missing key in the map. This requires
|
|||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=f
|
```pascaligo group=f
|
||||||
function force_access (const key : address; const moves : register) : move is
|
function force_access (const key : address; const moves : register) : move is
|
||||||
case moves[key] of
|
case moves[key] of
|
||||||
@ -409,7 +404,7 @@ function force_access (const key : address; const moves : register) : move is
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=f
|
```cameligo group=f
|
||||||
let force_access (key, moves : address * register) : move =
|
let force_access (key, moves : address * register) : move =
|
||||||
match Map.find_opt key moves with
|
match Map.find_opt key moves with
|
||||||
@ -417,7 +412,7 @@ let force_access (key, moves : address * register) : move =
|
|||||||
| None -> (failwith "No move." : move)
|
| None -> (failwith "No move." : move)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=f
|
```reasonligo group=f
|
||||||
let force_access = ((key, moves) : (address, register)) : move => {
|
let force_access = ((key, moves) : (address, register)) : move => {
|
||||||
switch (Map.find_opt (key, moves)) {
|
switch (Map.find_opt (key, moves)) {
|
||||||
@ -438,7 +433,7 @@ those operations are called *updates*.
|
|||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
The values of a PascaLIGO map can be updated using the usual
|
The values of a PascaLIGO map can be updated using the usual
|
||||||
assignment syntax `<map variable>[<key>] := <new value>`. Let us
|
assignment syntax `<map variable>[<key>] := <new value>`. Let us
|
||||||
@ -464,7 +459,7 @@ function assignments (var m : register) : register is
|
|||||||
} with m
|
} with m
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
We can update a binding in a map in CameLIGO by means of the
|
We can update a binding in a map in CameLIGO by means of the
|
||||||
`Map.update` built-in function:
|
`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
|
> use `None` instead, that would have meant that the binding is
|
||||||
> removed.
|
> removed.
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
We can update a binding in a map in ReasonLIGO by means of the
|
We can update a binding in a map in ReasonLIGO by means of the
|
||||||
`Map.update` built-in function:
|
`Map.update` built-in function:
|
||||||
@ -501,7 +496,7 @@ To remove a binding from a map, we need its key.
|
|||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
In PascaLIGO, there is a special instruction to remove a binding from
|
In PascaLIGO, there is a special instruction to remove a binding from
|
||||||
a map.
|
a map.
|
||||||
@ -512,13 +507,19 @@ function delete (const key : address; var moves : register) : register is
|
|||||||
} with moves
|
} with moves
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, we use the predefined function `Map.remove` as follows:
|
||||||
|
|
||||||
```cameligo group=f
|
```cameligo group=f
|
||||||
let delete (key, moves : address * register) : register =
|
let delete (key, moves : address * register) : register =
|
||||||
Map.remove key moves
|
Map.remove key moves
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
In ReasonLIGO, we use the predefined function `Map.remove` as follows:
|
||||||
|
|
||||||
```reasonligo group=f
|
```reasonligo group=f
|
||||||
let delete = ((key, moves) : (address, register)) : register => {
|
let delete = ((key, moves) : (address, register)) : register => {
|
||||||
Map.remove (key, moves);
|
Map.remove (key, moves);
|
||||||
@ -528,51 +529,85 @@ let delete = ((key, moves) : (address, register)) : register => {
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### Iterating Functionally over a Map
|
### Functional Iteration over Maps
|
||||||
|
|
||||||
A *functional iterator* is a function that traverses a data structure
|
A *functional iterator* is a function that traverses a data structure
|
||||||
and calls in turn a given function over the elements of that structure
|
and calls in turn a given function over the elements of that structure
|
||||||
to compute some value. Another approach is possible in PascaLIGO:
|
to compute some value. Another approach is possible in PascaLIGO:
|
||||||
*loops* (see the relevant section).
|
*loops* (see the relevant section).
|
||||||
|
|
||||||
There are three kinds of functional iteration over LIGO maps: `iter`,
|
There are three kinds of functional iterations over LIGO maps: the
|
||||||
`map` and `fold`. The first, `iter`, is an iteration over the map with
|
*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
|
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
|
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.
|
of a map is within a certain range, and fail with an error otherwise.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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
|
```pascaligo group=f
|
||||||
function iter_op (const m : register) : unit is
|
function iter_op (const m : register) : unit is
|
||||||
block {
|
block {
|
||||||
function aggregate (const i : address; const j : move) : unit is block
|
function iterated (const i : address; const j : move) : unit is
|
||||||
{ if j.1 > 1 then skip else failwith ("Below range.") } with unit
|
if j.1 > 3 then Unit else (failwith ("Below range.") : unit)
|
||||||
} with map_iter (aggregate, m)
|
} with map_iter (iterated, m)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
> The iterated function must be pure, that is, it cannot mutate
|
||||||
|
> variables.
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
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
|
```cameligo group=f
|
||||||
let iter_op (m : register) : unit =
|
let iter_op (m : register) : unit =
|
||||||
let assert_eq = fun (i,j : address * move) -> assert (j.0 > 1)
|
let predicate = fun (i,j : address * move) -> assert (j.0 > 3)
|
||||||
in Map.iter assert_eq m
|
in Map.iter predicate m
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
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
|
```reasonligo group=f
|
||||||
let iter_op = (m : register) : unit => {
|
let iter_op = (m : register) : unit => {
|
||||||
let assert_eq = ((i,j) : (address, move)) => assert (j[0] > 1);
|
let predicate = ((i,j) : (address, move)) => assert (j[0] > 3);
|
||||||
Map.iter (assert_eq, m);
|
Map.iter (predicate, m);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
#### Map Operation
|
||||||
|
|
||||||
We may want to change all the bindings of a map by applying to them a
|
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
|
function. This is called a *map operation*, not to be confused with
|
||||||
*map data structure* we have been presenting.
|
the map data structure.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
In PascaLIGO, the predefined functional iterator implementing the map
|
||||||
|
operation over maps is called `map_map`and is used as follows:
|
||||||
|
|
||||||
```pascaligo group=f
|
```pascaligo group=f
|
||||||
function map_op (const m : register) : register is
|
function map_op (const m : register) : register is
|
||||||
block {
|
block {
|
||||||
@ -581,49 +616,78 @@ function map_op (const m : register) : register is
|
|||||||
} with map_map (increment, m)
|
} with map_map (increment, m)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
> The mapped function must be pure, that is, it cannot mutate
|
||||||
|
> variables.
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, the predefined functional iterator implementing the map
|
||||||
|
operation over maps is called `Map.map` and is used as follows:
|
||||||
|
|
||||||
```cameligo group=f
|
```cameligo group=f
|
||||||
let map_op (m : register) : register =
|
let map_op (m : register) : register =
|
||||||
let increment = fun (i,j : address * move) -> j.0, j.1 + 1
|
let increment = fun (i,j : address * move) -> j.0, j.1 + 1
|
||||||
in Map.map increment m
|
in Map.map increment m
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
In ReasonLIGO, the predefined functional iteratir implementing the map
|
||||||
|
operation over maps is called `Map.map` and is used as follows:
|
||||||
|
|
||||||
```reasonligo group=f
|
```reasonligo group=f
|
||||||
let map_op = (m : register) : register => {
|
let map_op = (m : register) : register => {
|
||||||
let increment = ((i,j): (address, move)) => (j[0], j[1] + 1);
|
let increment = ((i,j): (address, move)) => (j[0], j[1] + 1);
|
||||||
Map.map(increment, m);
|
Map.map (increment, m);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
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
|
function takes two arguments: an *accumulator* and the structure
|
||||||
*element* at hand, with which it then produces a new accumulator. This
|
*element* at hand, with which it then produces a new accumulator. This
|
||||||
enables having 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.
|
traversal of the data structure is over.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
In PascaLIGO, the predefined functional iterator implementing the fold
|
||||||
|
operation over maps is called `map_fold` and is used as follows:
|
||||||
|
|
||||||
```pascaligo group=f
|
```pascaligo group=f
|
||||||
function fold_op (const m : register) : int is block {
|
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
|
j + cur.1.1
|
||||||
} with map_fold (aggregate, m, 5)
|
} with map_fold (iterated, m, 5)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
> The folded function must be pure, that is, it cannot mutate
|
||||||
|
> variables.
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, the predefined functional iterator implementing the fold
|
||||||
|
operation over maps is called `Map.fold` and is used as follows:
|
||||||
|
|
||||||
```cameligo group=f
|
```cameligo group=f
|
||||||
let fold_op (m : register) : register =
|
let fold_op (m : register) : register =
|
||||||
let aggregate = fun (i,j : int * (address * move)) -> i + j.1.1
|
let iterated = fun (i,j : int * (address * move)) -> i + j.1.1
|
||||||
in Map.fold aggregate m 5
|
in Map.fold iterated m 5
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
In ReasonLIGO, the predefined functional iterator implementing the
|
||||||
|
fold operation over maps is called `Map.fold` and is used as follows:
|
||||||
|
|
||||||
```reasonligo group=f
|
```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];
|
let iterated = ((i,j): (int, (address, move))) => i + j[1][1];
|
||||||
Map.fold (aggregate, m, 5);
|
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:
|
Here is how we define a big map:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=g
|
```pascaligo group=g
|
||||||
type move is int * int
|
type move is int * int
|
||||||
|
|
||||||
type register is big_map (address, move)
|
type register is big_map (address, move)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=g
|
```cameligo group=g
|
||||||
type move = int * int
|
type move = int * int
|
||||||
|
|
||||||
type register = (address, move) big_map
|
type register = (address, move) big_map
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=g
|
```reasonligo group=g
|
||||||
type move = (int, int);
|
type move = (int, int);
|
||||||
|
|
||||||
type register = big_map(address, move);
|
type register = big_map (address, move);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
And here is how a map value is created:
|
And here is how a map value is created:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
```pascaligo group=g
|
```pascaligo group=g
|
||||||
const moves : register =
|
const moves : register =
|
||||||
@ -682,7 +746,7 @@ const moves : register =
|
|||||||
> `("<string value>" : address)` means that we cast a string into an
|
> `("<string value>" : address)` means that we cast a string into an
|
||||||
> address.
|
> address.
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
```cameligo group=g
|
```cameligo group=g
|
||||||
let moves : register =
|
let moves : register =
|
||||||
@ -696,7 +760,7 @@ let moves : register =
|
|||||||
> separating individual map entries. The annotated value `("<string
|
> separating individual map entries. The annotated value `("<string
|
||||||
> value>" : address)` means that we cast a string into an address.
|
> value>" : address)` means that we cast a string into an address.
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
```reasonligo group=g
|
```reasonligo group=g
|
||||||
let moves : register =
|
let moves : register =
|
||||||
@ -713,28 +777,30 @@ let moves : register =
|
|||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
### Accessing Values by Key
|
### Accessing Values
|
||||||
|
|
||||||
If we want to access a move from our `register` above, we can use the
|
If we want to access a move from our `register` above, we can use the
|
||||||
postfix `[]` operator to read the associated `move` value. However,
|
postfix `[]` operator to read the associated `move` value. However,
|
||||||
the value we read is an optional value: in our case, of type `option
|
the value we read is an optional value (in our case, of type `option
|
||||||
(move)`. Here is an example:
|
(move)`), to account for a missing key. Here is an example:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
```pascaligo group=g
|
```pascaligo group=g
|
||||||
const my_balance : option (move) =
|
const my_balance : option (move) =
|
||||||
moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)]
|
moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
```cameligo group=g
|
```cameligo group=g
|
||||||
let my_balance : move option =
|
let my_balance : move option =
|
||||||
Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves
|
Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
```reasonligo group=g
|
```reasonligo group=g
|
||||||
let my_balance : option (move) =
|
let my_balance : option (move) =
|
||||||
@ -742,23 +808,25 @@ let my_balance : option (move) =
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
### Updating a Big Map
|
### Updating Big Maps
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
The values of a PascaLIGO big map can be updated using the
|
The values of a PascaLIGO big map can be updated using the
|
||||||
assignment syntax for ordinary maps
|
assignment syntax for ordinary maps
|
||||||
|
|
||||||
```pascaligo group=g
|
```pascaligo group=g
|
||||||
function assign (var m : register) : register is
|
function add (var m : register) : register is
|
||||||
block {
|
block {
|
||||||
m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9)
|
m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9)
|
||||||
} with m
|
} with m
|
||||||
|
|
||||||
|
const updated_map : register = add (moves)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
We can update a big map in CameLIGO using the `Big_map.update`
|
We can update a big map in CameLIGO using the `Big_map.update`
|
||||||
built-in:
|
built-in:
|
||||||
@ -769,7 +837,7 @@ let updated_map : register =
|
|||||||
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) moves
|
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) moves
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Reasonligo-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
We can update a big map in ReasonLIGO using the `Big_map.update`
|
We can update a big map in ReasonLIGO using the `Big_map.update`
|
||||||
built-in:
|
built-in:
|
||||||
@ -780,6 +848,47 @@ let updated_map : register =
|
|||||||
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves);
|
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Removing Bindings from a Map
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
### Removing Bindings
|
||||||
|
|
||||||
|
Removing a binding in a map is done differently according to the LIGO
|
||||||
|
syntax.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
PascaLIGO features a special syntactic construct to remove bindings
|
||||||
|
from maps, of the form `remove <key> from map <map>`. For example,
|
||||||
|
|
||||||
|
```pascaligo group=g
|
||||||
|
function rem (var m : register) : register is
|
||||||
|
block {
|
||||||
|
remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) from map moves
|
||||||
|
} with m
|
||||||
|
|
||||||
|
const updated_map : register = rem (moves)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, the predefined function which removes a binding in a map
|
||||||
|
is called `Map.remove` and is used as follows:
|
||||||
|
|
||||||
|
```cameligo group=g
|
||||||
|
let updated_map : register =
|
||||||
|
Map.remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
In ReasonLIGO, the predefined function which removes a binding in a map
|
||||||
|
is called `Map.remove` and is used as follows:
|
||||||
|
|
||||||
|
```reasonligo group=g
|
||||||
|
let updated_map : register =
|
||||||
|
Map.remove (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves)
|
||||||
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -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.
|
that adding an integer to a natural number produces an integer.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
// int + int yields int
|
// int + int yields int
|
||||||
const a : int = 5 + 10
|
const a : int = 5 + 10
|
||||||
@ -50,7 +49,6 @@ const g : int = 1_000_000
|
|||||||
>```
|
>```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
// int + int yields int
|
// int + int yields int
|
||||||
let a : int = 5 + 10
|
let a : int = 5 + 10
|
||||||
@ -81,7 +79,6 @@ let g : int = 1_000_000
|
|||||||
>```
|
>```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
// int + int yields int
|
// int + int yields int
|
||||||
let a : int = 5 + 10;
|
let a : int = 5 + 10;
|
||||||
@ -99,7 +96,7 @@ let c : tez = 5mutez + 10mutez;
|
|||||||
let e : nat = 5n + 10n;
|
let e : nat = 5n + 10n;
|
||||||
|
|
||||||
// nat + int yields an int: invalid
|
// nat + int yields an int: invalid
|
||||||
//let f : nat = 5n + 10;
|
// let f : nat = 5n + 10;
|
||||||
|
|
||||||
let g : int = 1_000_000;
|
let g : int = 1_000_000;
|
||||||
```
|
```
|
||||||
@ -109,7 +106,6 @@ let g : int = 1_000_000;
|
|||||||
>```reasonligo
|
>```reasonligo
|
||||||
>let sum : tex = 100_000mutez;
|
>let sum : tex = 100_000mutez;
|
||||||
>```
|
>```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Subtraction
|
## Subtraction
|
||||||
@ -119,7 +115,7 @@ Subtraction looks as follows.
|
|||||||
> ⚠️ Even when subtracting two `nats`, the result is an `int`
|
> ⚠️ Even when subtracting two `nats`, the result is an `int`
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
const a : int = 5 - 10
|
const a : int = 5 - 10
|
||||||
|
|
||||||
@ -166,7 +162,7 @@ let d : tez = 5mutez - 1mutez;
|
|||||||
You can multiply values of the same type, such as:
|
You can multiply values of the same type, such as:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const a : int = 5 * 5
|
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`
|
> ⚠️ Division of two `tez` values results into a `nat`
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=d
|
```pascaligo group=d
|
||||||
const a : int = 10 / 3
|
const a : int = 10 / 3
|
||||||
const b : nat = 10n / 3n
|
const b : nat = 10n / 3n
|
||||||
@ -248,11 +244,11 @@ let b : nat = abs (1);
|
|||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Check if a value is a `nat`
|
## Checking a `nat`
|
||||||
|
|
||||||
You can check if a value is a `nat` by using a syntax specific
|
You can check if a value is a `nat` by using a predefined cast
|
||||||
built-in function, which accepts an `int` and returns an optional
|
function which accepts an `int` and returns an optional `nat`: if the
|
||||||
`nat`: if `Some(nat)` then the provided integer was indeed a natural
|
result is not `None`, then the provided integer was indeed a natural
|
||||||
number, and not otherwise.
|
number, and not otherwise.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,11 +3,8 @@ id: sets-lists-tuples
|
|||||||
title: Tuples, Lists, Sets
|
title: Tuples, Lists, Sets
|
||||||
---
|
---
|
||||||
|
|
||||||
Apart from complex data types such as `maps` and `records`, ligo also
|
Apart from complex data types such as `maps` and `records`, LIGO also
|
||||||
exposes `tuples`, `lists` and `sets`.
|
features `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
|
||||||
|
|
||||||
@ -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
|
values, called *components*, can be retrieved by their index
|
||||||
(position). Probably the most common tuple is the *pair*. For
|
(position). Probably the most common tuple is the *pair*. For
|
||||||
example, if we were storing coordinates on a two dimensional grid we
|
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
|
might use a pair `(x,y)` to store the coordinates `x` and `y`. There
|
||||||
`y` as the pair value `(x,y)`. There is a *specific order*, so `(y,x)`
|
is a *specific order*, so `(y,x)` is not equal to `(x,y)`. The number
|
||||||
is not equal to `(x,y)`. The number of components is part of the type
|
of components is part of the type of a tuple, so, for example, we
|
||||||
of a tuple, so, for example, we cannot add an extra component to a
|
cannot add an extra component to a pair and obtain a triple of the
|
||||||
pair and obtain a triple of the same type: `(x,y)` has always a
|
same type, so, for instance, `(x,y)` has always a different type from
|
||||||
different type from `(x,y,z)`, whereas `(y,x)` may have the same type.
|
`(x,y,z)`, whereas `(y,x)` might have the same type as `(x,y)`.
|
||||||
|
|
||||||
Like records, tuple components can be of arbitrary types.
|
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
|
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
|
have to be defined before they can be used. However below we will give
|
||||||
@ -33,54 +30,55 @@ them names by *type aliasing*.
|
|||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=c
|
|
||||||
|
```pascaligo group=tuple
|
||||||
type full_name is string * string // Alias
|
type full_name is string * string // Alias
|
||||||
|
|
||||||
const full_name : full_name = ("Alice", "Johnson")
|
const full_name : full_name = ("Alice", "Johnson")
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
|
||||||
|
```cameligo group=tuple
|
||||||
type full_name = string * string // Alias
|
type full_name = string * string // Alias
|
||||||
|
|
||||||
(* The parenthesis here are optional *)
|
let full_name : full_name = ("Alice", "Johnson") // Optional parentheses
|
||||||
let full_name : full_name = ("Alice", "Johnson")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
|
||||||
|
```reasonligo group=tuple
|
||||||
type full_name = (string, string); // Alias
|
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");
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### Accessing an Element in a Tuple
|
### Accessing Components
|
||||||
|
|
||||||
Accessing the components of a tuple in OCaml is achieved by
|
Accessing the components of a tuple in OCaml is achieved by
|
||||||
[pattern matching](language-basics/unit-option-pattern-matching.md). LIGO
|
[pattern matching](language-basics/unit-option-pattern-matching.md). LIGO
|
||||||
currently supports tuple patterns only in the parameters of functions,
|
currently supports tuple patterns only in the parameters of functions,
|
||||||
not in pattern matching. In LIGO, however, we can access components by
|
not in pattern matching. However, we can access components by their
|
||||||
their position in their tuple, which cannot be done in OCaml.
|
position in their tuple, which cannot be done in OCaml.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
Tuple components are one-indexed like so:
|
Tuple components are one-indexed like so:
|
||||||
|
|
||||||
```pascaligo group=c
|
```pascaligo group=tuple
|
||||||
const first_name : string = full_name.1;
|
const first_name : string = full_name.1;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
Tuple elements are zero-indexed and accessed like so:
|
Tuple elements are zero-indexed and accessed like so:
|
||||||
|
|
||||||
```cameligo group=c
|
```cameligo group=tuple
|
||||||
let first_name : string = full_name.0
|
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:
|
Tuple components are one-indexed like so:
|
||||||
|
|
||||||
```reasonligo group=c
|
```reasonligo group=tuple
|
||||||
let first_name : string = full_name[1];
|
let first_name : string = full_name[1];
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Lists
|
## Lists
|
||||||
|
|
||||||
Lists are linear collections of elements of the same type. Linear
|
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
|
> 💡 Lists are useful when returning operations from a smart
|
||||||
> contract's entrypoint.
|
> contract's entrypoint.
|
||||||
|
|
||||||
### Defining a List
|
### Defining Lists
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=lists
|
||||||
const my_list : list (int) = list [1; 2; 2] // The head is 1
|
const my_list : list (int) = list [1; 2; 2] // The head is 1
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=b
|
```cameligo group=lists
|
||||||
let my_list : int list = [1; 2; 2] // The head is 1
|
let my_list : int list = [1; 2; 2] // The head is 1
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=b
|
```reasonligo group=lists
|
||||||
let my_list : list (int) = [1, 2, 2]; // The head is 1
|
let my_list : list (int) = [1, 2, 2]; // The head is 1
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### Adding to a List
|
### Adding to Lists
|
||||||
|
|
||||||
|
|
||||||
Lists can be augmented by adding an element before the head (or, in
|
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
|
terms of stack, by *pushing an element on top*). This operation is
|
||||||
usually called *consing* in functional languages.
|
usually called *consing* in functional languages.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
In PascaLIGO, the *cons operator* is infix and noted `#`. It is not
|
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
|
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
|
list on which to cons. (The symbol is helpfully asymmetric to remind
|
||||||
you of that.)
|
you of that.)
|
||||||
|
|
||||||
|
```pascaligo group=lists
|
||||||
|
const larger_list : list (int) = 5 # my_list // [5;1;2;2]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
In CameLIGO, the *cons operator* is infix and noted `::`. It is not
|
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
|
symmetric: on the left lies the element to cons, and, on the right, a
|
||||||
list on which to cons.
|
list on which to cons.
|
||||||
|
|
||||||
|
```cameligo group=lists
|
||||||
|
let larger_list : int list = 5 :: my_list // [5;1;2;2]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
In ReasonLIGO, the *cons operator* is infix and noted `, ...`. It is
|
In ReasonLIGO, the *cons operator* is infix and noted `, ...`. It is
|
||||||
not symmetric: on the left lies the element to cons, and, on the
|
not symmetric: on the left lies the element to cons, and, on the
|
||||||
right, a list on which to cons.
|
right, a list on which to cons.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```reasonligo group=lists
|
||||||
<!--Pascaligo-->
|
let larger_list : list (int) = [5, ...my_list]; // [5,1,2,2]
|
||||||
```pascaligo group=b
|
|
||||||
const larger_list : list (int) = 5 # my_list
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=b
|
|
||||||
let larger_list : int list = 5 :: my_list
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=b
|
|
||||||
let larger_list : list (int) = [5, ...my_list];
|
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
> 💡 Lists can be iterated, folded or mapped to different values. You
|
> 💡 Lists can be iterated, folded or mapped to different values. You
|
||||||
> can find additional examples
|
> can find additional examples
|
||||||
> [here](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts)
|
> [here](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts)
|
||||||
> and other built-in operators
|
> and other built-in operators
|
||||||
> [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L59)
|
> [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
|
A *functional iterator* is a function that traverses a data structure
|
||||||
obtain the resulting list, in the same order. For example, we may want
|
and calls in turn a given function over the elements of that structure
|
||||||
to create a list that contains all the elements of another list
|
to compute some value. Another approach is possible in PascaLIGO:
|
||||||
incremented by one. This is a special case of *fold operation* called
|
*loops* (see the relevant section).
|
||||||
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`.
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=b
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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
|
function increment (const i : int): int is i + 1
|
||||||
|
|
||||||
// Creates a new list with all elements incremented by 1
|
// Creates a new list with all elements incremented by 1
|
||||||
const plus_one : list (int) = list_map (increment, larger_list)
|
const plus_one : list (int) = list_map (increment, larger_list)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```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
|
let increment (i : int) : int = i + 1
|
||||||
|
|
||||||
// Creates a new list with all elements incremented by 1
|
// Creates a new list with all elements incremented by 1
|
||||||
let plus_one : int list = List.map increment larger_list
|
let plus_one : int list = List.map increment larger_list
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```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;
|
let increment = (i : int) : int => i + 1;
|
||||||
|
|
||||||
// Creates a new list with all elements incremented by 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);
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### 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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=b
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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
|
function sum (const acc : int; const i : int): int is acc + i
|
||||||
|
|
||||||
const sum_of_elements : int = list_fold (sum, my_list, 0)
|
const sum_of_elements : int = list_fold (sum, my_list, 0)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> The folded function must be pure, that is, it cannot mutate
|
||||||
|
> variables.
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
```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 (acc, i: int * int) : int = acc + i
|
||||||
let sum_of_elements : int = List.fold sum my_list 0
|
let sum_of_elements : int = List.fold sum my_list 0
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
```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 = ((result, i): (int, int)): int => result + i;
|
||||||
let sum_of_elements : int = List.fold (sum, my_list, 0);
|
let sum_of_elements : int = List.fold (sum, my_list, 0);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Sets
|
## Sets
|
||||||
|
|
||||||
Sets are unordered collections of values of the same type, like lists
|
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
|
### Empty Sets
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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 []
|
const my_set : set (int) = set []
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```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-->
|
<!--ReasonLIGO-->
|
||||||
```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;
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
### Non-empty Sets
|
### Non-empty Sets
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
In PascaLIGO, the notation for sets is similar to that for lists,
|
In PascaLIGO, the notation for sets is similar to that for lists,
|
||||||
except the keyword `set` is used before:
|
except the keyword `set` is used before:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```pascaligo group=sets
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
const my_set : set (int) = set [3; 2; 2; 1]
|
const my_set : set (int) = set [3; 2; 2; 1]
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
You can check that `2` is not repeated in `my_set` by using the LIGO
|
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
|
compiler like this (the output will sort the elements of the set, but
|
||||||
that order is not significant for the compiler):
|
that order is not significant for the compiler):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
ligo evaluate-value
|
ligo evaluate-value
|
||||||
gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo my_set
|
gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo my_set
|
||||||
# Outputs: { 3 ; 2 ; 1 }
|
# Outputs: { 3 ; 2 ; 1 }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
In CameLIGO, there is no predefined syntactic construct for sets: you
|
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
|
must build your set by adding to the empty set. (This is the way in
|
||||||
OCaml.)
|
OCaml.)
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```cameligo group=sets
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=a
|
|
||||||
let my_set : int set =
|
let my_set : int set =
|
||||||
Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set))))
|
Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set))))
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
You can check that `2` is not repeated in `my_set` by using the LIGO
|
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
|
compiler like this (the output will sort the elements of the set, but
|
||||||
that order is not significant for the compiler):
|
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 }
|
# Outputs: { 3 ; 2 ; 1 }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
In ReasonLIGO, there is no predefined syntactic construct for sets:
|
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
|
you must build your set by adding to the empty set. (This is the way
|
||||||
in OCaml.)
|
in OCaml.)
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```reasonligo group=sets
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=a
|
|
||||||
let my_set : set (int) =
|
let my_set : set (int) =
|
||||||
Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int)))));
|
Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int)))));
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
You can check that `2` is not repeated in `my_set` by using the LIGO
|
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
|
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
|
gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo my_set
|
||||||
# Outputs: { 3 ; 2 ; 1 }
|
# Outputs: { 3 ; 2 ; 1 }
|
||||||
```
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
### Set Membership
|
### Set Membership
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
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.
|
infix operator checking membership in a set.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```pascaligo group=sets
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
const contains_3 : bool = my_set contains 3
|
const contains_3 : bool = my_set contains 3
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```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
|
let contains_3 : bool = Set.mem 3 my_set
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```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);
|
let contains_3 : bool = Set.mem (3, my_set);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
@ -357,37 +472,50 @@ let contains_3 : bool = Set.mem (3, my_set);
|
|||||||
### Cardinal
|
### Cardinal
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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)
|
const set_size : nat = size (my_set)
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```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
|
let set_size : nat = Set.size my_set
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```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);
|
let set_size : nat = Set.size (my_set);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### Adding or Removing from a Set
|
### Updating Sets
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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 larger_set : set (int) = set_add (4, my_set)
|
||||||
|
|
||||||
const smaller_set : set (int) = set_remove (3, my_set)
|
const smaller_set : set (int) = set_remove (3, my_set)
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
If we are in a block, we can use an instruction to modify the 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
|
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
|
that is modified in-place, and the other given as a literal
|
||||||
(extensional definition).
|
(extensional definition).
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```pascaligo group=sets
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
function update (var s : set (int)) : set (int) is block {
|
function update (var s : set (int)) : set (int) is block {
|
||||||
patch s with set [4; 7]
|
patch s with set [4; 7]
|
||||||
} with s
|
} with s
|
||||||
|
|
||||||
const new_set : set (int) = update (my_set)
|
const new_set : set (int) = update (my_set)
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
In CameLIGO, we update a given set by creating another one, with or
|
In CameLIGO, we update a given set by creating another one, with or
|
||||||
without some elements.
|
without some elements.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```cameligo group=sets
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=a
|
|
||||||
let larger_set : int set = Set.add 4 my_set
|
let larger_set : int set = Set.add 4 my_set
|
||||||
|
|
||||||
let smaller_set : int set = Set.remove 3 my_set
|
let smaller_set : int set = Set.remove 3 my_set
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
In ReasonLIGO, we update a given set by creating another one, with or
|
In ReasonLIGO, we update a given set by creating another one, with or
|
||||||
without some elements.
|
without some elements.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```reasonligo group=sets
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=a
|
|
||||||
let larger_set : set (int) = Set.add (4, my_set);
|
let larger_set : set (int) = Set.add (4, my_set);
|
||||||
|
|
||||||
let smaller_set : set (int) = Set.remove (3, 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);
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### 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
|
There are three kinds of functional iterations over LIGO maps: the
|
||||||
elements it contains, while accumulating some value which is returned
|
*iterated operation*, the *map operation* (not to be confused with the
|
||||||
at the end. This is a *fold operation*. In the following example, we
|
*map data structure*) and the *fold operation*.
|
||||||
sum up all the elements of the set `my_set` defined above.
|
|
||||||
|
|
||||||
|
#### Iterated Operation
|
||||||
|
|
||||||
In PascaLIGO, the folded function takes the accumulator first and the
|
The first, the *iterated operation*, is an iteration over the map with
|
||||||
(current) set element second. The predefined fold is called `set_fold`.
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
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);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
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
|
function sum (const acc : int; const i : int): int is acc + i
|
||||||
|
|
||||||
const sum_of_elements : int = set_fold (sum, my_set, 0)
|
const sum_of_elements : int = set_fold (sum, my_set, 0)
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
> The folded function must be pure, that is, it cannot mutate
|
||||||
|
> variables.
|
||||||
|
|
||||||
It is possible to use a *loop* over a set as well.
|
It is possible to use a *loop* over a set as well.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```pascaligo group=sets
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
function loop (const s : set (int)) : int is block {
|
function loop (const s : set (int)) : int is block {
|
||||||
var sum : int := 0;
|
var sum : int := 0;
|
||||||
for element in set s block {
|
for element in set s block {
|
||||||
@ -471,27 +710,24 @@ function loop (const s : set (int)) : int is block {
|
|||||||
}
|
}
|
||||||
} with sum
|
} with sum
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
In CameLIGO, the predefined fold over sets is called `Set.fold`.
|
In CameLIGO, the predefined fold over sets is called `Set.fold`.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```cameligo group=sets
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=a
|
|
||||||
let sum (acc, i : int * int) : int = acc + i
|
let sum (acc, i : int * int) : int = acc + i
|
||||||
|
|
||||||
let sum_of_elements : int = Set.fold sum my_set 0
|
let sum_of_elements : int = Set.fold sum my_set 0
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
In ReasonLIGO, the predefined fold over sets is called `Set.fold`.
|
In ReasonLIGO, the predefined fold over sets is called `Set.fold`.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
```reasonligo group=sets
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=a
|
|
||||||
let sum = ((acc, i) : (int, int)) : int => acc + i;
|
let sum = ((acc, i) : (int, int)) : int => acc + i;
|
||||||
|
|
||||||
let sum_of_elements : int = Set.fold (sum, my_set, 0);
|
let sum_of_elements : int = Set.fold (sum, my_set, 0);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ title: Strings
|
|||||||
Strings are defined using the built-in `string` type like this:
|
Strings are defined using the built-in `string` type like this:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```
|
```
|
||||||
const a : string = "Hello Alice"
|
const a : string = "Hello Alice"
|
||||||
```
|
```
|
||||||
@ -21,10 +21,10 @@ let a : string = "Hello Alice";
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Concatenating strings
|
## Concatenating Strings
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
Strings can be concatenated using the `^` operator.
|
Strings can be concatenated using the `^` operator.
|
||||||
|
|
||||||
```pascaligo
|
```pascaligo
|
||||||
@ -51,12 +51,12 @@ let full_greeting : string = greeting ++ " " ++ name;
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Slicing strings
|
## Slicing Strings
|
||||||
|
|
||||||
Strings can be sliced using a built-in function:
|
Strings can be sliced using a built-in function:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo
|
||||||
const name : string = "Alice"
|
const name : string = "Alice"
|
||||||
const slice : string = string_slice (0n, 1n, name)
|
const slice : string = string_slice (0n, 1n, name)
|
||||||
@ -73,29 +73,27 @@ let slice : string = String.slice (0n, 1n, name);
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
> ⚠️ Notice that the `offset` and slice `length` are natural numbers
|
> ⚠️ Notice that the offset and length of the slice are natural numbers.
|
||||||
> (`nat`).
|
|
||||||
|
|
||||||
## Aquiring the length of a string
|
## Length of Strings
|
||||||
|
|
||||||
The length of a string can be found using a built-in function:
|
The length of a string can be found using a built-in function:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo
|
||||||
const name : string = "Alice"
|
const name : string = "Alice"
|
||||||
// length = 5
|
const length : nat = size (name) // length = 5
|
||||||
const length : nat = size (name)
|
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo
|
||||||
let name : string = "Alice"
|
let name : string = "Alice"
|
||||||
let length : nat = String.size name
|
let length : nat = String.size name // length = 5
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo
|
||||||
let name : string = "Alice";
|
let name : string = "Alice";
|
||||||
let length : nat = String.size (name);
|
let length : nat = String.size (name); // length == 5
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -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
|
would be a little odd if it did not have any Tezos specific
|
||||||
functions. This page will tell you about them.
|
functions. This page will tell you about them.
|
||||||
|
|
||||||
## Pack and unpack
|
## Pack and Unpack
|
||||||
|
|
||||||
Michelson provides the `PACK` and `UNPACK` instructions for data
|
Michelson provides the `PACK` and `UNPACK` instructions for data
|
||||||
serialization. The instruction `PACK` converts Michelson data
|
serialization. The instruction `PACK` converts Michelson data
|
||||||
@ -59,11 +59,12 @@ predefined function returning a value of type `key_hash`.
|
|||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is block {
|
function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is
|
||||||
var ret : bool := False;
|
block {
|
||||||
var kh2 : key_hash := crypto_hash_key (k2);
|
var ret : bool := False;
|
||||||
if kh1 = kh2 then ret := True else skip
|
var kh2 : key_hash := crypto_hash_key (k2);
|
||||||
} with (ret, kh2)
|
if kh1 = kh2 then ret := True else skip
|
||||||
|
} with (ret, kh2)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
@ -122,14 +123,14 @@ let check_signature =
|
|||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Getting the contract's own address
|
## Contract's Own Address
|
||||||
|
|
||||||
Often you want to get the address of the contract being executed. You
|
Often you want to get the address of the contract being executed. You
|
||||||
can do it with `self_address`.
|
can do it with `self_address`.
|
||||||
|
|
||||||
> ⚠️ Due to limitations in Michelson, `self_address` in a contract is
|
> ⚠️ 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
|
> only allowed at the entrypoint level, that is, at the
|
||||||
> an auxiliaru function will cause an error.
|
> top-level. Using it in an embedded function will cause an error.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@ id: types
|
|||||||
title: Types
|
title: Types
|
||||||
---
|
---
|
||||||
|
|
||||||
LIGO is strongly and statically typed. This means that the compiler
|
*LIGO is strongly and statically typed.* This means that the compiler
|
||||||
checks your program at compilation time and, if it passes the tests,
|
checks how your contract processes data. If it passes the test, your
|
||||||
this ensures that there will be no runtime error due to wrong
|
contract will not fail at run-time due to inconsistent assumptions on
|
||||||
assumptions on the data. This is called *type checking*.
|
your data. This is called *type checking*.
|
||||||
|
|
||||||
LIGO types are built on top of Michelson's type system.
|
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.
|
comunicate our intent with added clarity.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
type breed is string
|
type breed is string
|
||||||
const dog_breed : breed = "Saluki"
|
const dog_breed : breed = "Saluki"
|
||||||
@ -51,15 +51,14 @@ let dog_breed : breed = "Saluki";
|
|||||||
## Simple types
|
## Simple types
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
// The type accountBalances denotes maps from addresses to tez
|
// The type accountBalances denotes maps from addresses to tez
|
||||||
|
|
||||||
type account_balances is map (address, tez)
|
type account_balances is map (address, tez)
|
||||||
|
|
||||||
const ledger : account_balances =
|
const ledger : account_balances =
|
||||||
map
|
map [("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez]
|
||||||
[("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.
|
the balance and number of previous transactions for a given account.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
// Type aliasing
|
// Type aliasing
|
||||||
|
|
||||||
type account is address
|
type account is address
|
||||||
type number_of_transactions is nat
|
type number_of_transactions is nat
|
||||||
|
|
||||||
// The type account_data is a record with two fields.
|
// The type account_data is a record with two fields.
|
||||||
|
|
||||||
type account_data is record [
|
type account_data is record [
|
||||||
balance : tez;
|
balance : tez;
|
||||||
transactions : number_of_transactions
|
transactions : number_of_transactions
|
||||||
]
|
]
|
||||||
|
|
||||||
// A ledger is a map from accounts to account_data
|
// A ledger is a map from accounts to account_data
|
||||||
|
|
||||||
type ledger is map (account, account_data)
|
type ledger is map (account, account_data)
|
||||||
|
|
||||||
const my_ledger : ledger = map [
|
const my_ledger : ledger = map [
|
||||||
@ -126,14 +128,19 @@ const my_ledger : ledger = map [
|
|||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
// Type aliasing
|
// Type aliasing
|
||||||
|
|
||||||
type account = address
|
type account = address
|
||||||
type number_of_transactions = nat
|
type number_of_transactions = nat
|
||||||
|
|
||||||
// The type account_data is a record with two fields.
|
// The type account_data is a record with two fields.
|
||||||
|
|
||||||
type account_data = {
|
type account_data = {
|
||||||
balance : tez;
|
balance : tez;
|
||||||
transactions : number_of_transactions
|
transactions : number_of_transactions
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ledger is a map from accounts to account_data
|
// A ledger is a map from accounts to account_data
|
||||||
|
|
||||||
type ledger = (account, account_data) map
|
type ledger = (account, account_data) map
|
||||||
|
|
||||||
let my_ledger : ledger = Map.literal
|
let my_ledger : ledger = Map.literal
|
||||||
@ -144,16 +151,19 @@ let my_ledger : ledger = Map.literal
|
|||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
// Type aliasing
|
// Type aliasing
|
||||||
|
|
||||||
type account = address;
|
type account = address;
|
||||||
type number_of_transactions = nat;
|
type number_of_transactions = nat;
|
||||||
|
|
||||||
// The type account_data is a record with two fields.
|
// The type account_data is a record with two fields.
|
||||||
|
|
||||||
type account_data = {
|
type account_data = {
|
||||||
balance : tez,
|
balance : tez,
|
||||||
transactions : number_of_transactions
|
transactions : number_of_transactions
|
||||||
};
|
};
|
||||||
|
|
||||||
// A ledger is a map from accounts to account_data
|
// A ledger is a map from accounts to account_data
|
||||||
|
|
||||||
type ledger = map (account, account_data);
|
type ledger = map (account, account_data);
|
||||||
|
|
||||||
let my_ledger : ledger =
|
let my_ledger : ledger =
|
||||||
@ -173,19 +183,91 @@ exclusive to each other).
|
|||||||
## Annotations
|
## Annotations
|
||||||
|
|
||||||
In certain cases, the type of an expression cannot be properly
|
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:
|
annotate an expression with its desired type. Here is an example:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo
|
|
||||||
type int_map is map (int, int)
|
|
||||||
|
|
||||||
function get_first (const my_map : int_map): option (int) is my_map[1]
|
<!--PascaLIGO-->
|
||||||
|
```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-->
|
||||||
|
```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-->
|
||||||
|
```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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -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
|
instances of a more general kind of types: *variant types* (sometimes
|
||||||
called *sum types*).
|
called *sum types*).
|
||||||
|
|
||||||
## The unit type
|
## The unit Type
|
||||||
|
|
||||||
The `unit` type in Michelson or LIGO is a predefined type that
|
The `unit` type in Michelson or LIGO is a predefined type that
|
||||||
contains only one value that carries no information. It is used when
|
contains only one value that carries no information. It is used when
|
||||||
no relevant information is required or produced. Here is how it used.
|
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.
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
In PascaLIGO, the unique value of the `unit` type is `Unit`.
|
In PascaLIGO, the unique value of the `unit` type is `Unit`.
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const n : unit = Unit
|
const n : unit = Unit // Note the capital letter
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
@ -56,11 +53,11 @@ Here is how we define a coin as being either head or tail (and nothing
|
|||||||
else):
|
else):
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
type coin is Head | Tail
|
type coin is Head | Tail
|
||||||
const head : coin = Head (Unit) // Unit needed because of a bug
|
const head : coin = Head (Unit) // Unit needed for now.
|
||||||
const tail : coin = Tail (Unit) // Unit needed because of a bug
|
const tail : coin = Tail (Unit) // Unit needed for now.
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
@ -87,7 +84,7 @@ define different kinds of users of a system.
|
|||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
type id is nat
|
type id is nat
|
||||||
|
|
||||||
@ -128,9 +125,6 @@ let g : user = Guest;
|
|||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
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
|
## Optional values
|
||||||
|
|
||||||
@ -143,7 +137,7 @@ meaningful value *of any type*. An example in arithmetic is the
|
|||||||
division operation:
|
division operation:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=d
|
```pascaligo group=d
|
||||||
function div (const a : nat; const b : nat) : option (nat) is
|
function div (const a : nat; const b : nat) : option (nat) is
|
||||||
if b = 0n then (None: option (nat)) else Some (a/b)
|
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
|
*Pattern matching* is similiar to the `switch` construct in
|
||||||
Javascript, and can be used to route the program's control flow based
|
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
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=e
|
```pascaligo group=e
|
||||||
type coin is Head | Tail
|
type coin is Head | Tail
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ declaration. When defining a constant you need to provide a `name`,
|
|||||||
`type` and a `value`:
|
`type` and a `value`:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const age : int = 25
|
const age : int = 25
|
||||||
```
|
```
|
||||||
@ -53,14 +53,11 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constant
|
|||||||
## Variables
|
## Variables
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
Variables, unlike constants, are mutable. They cannot be declared in a
|
Variables, unlike constants, are *mutable*. They cannot be declared in
|
||||||
*global scope*, but they can be declared and used within functions, or
|
a *global scope*, but they can be declared and used within functions,
|
||||||
as function parameters.
|
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.
|
|
||||||
|
|
||||||
> ⚠️ Please be wary that mutation only works within the function scope
|
> ⚠️ Please be wary that mutation only works within the function scope
|
||||||
> itself, values outside of the function scope will not be affected.
|
> itself, values outside of the function scope will not be affected.
|
||||||
@ -68,7 +65,7 @@ as function parameters.
|
|||||||
|
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
// The following is invalid: use `const` for global values instead.
|
// 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
|
function add (const a : int; const b : int) : int is
|
||||||
block {
|
block {
|
||||||
@ -90,11 +87,8 @@ ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/
|
|||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
As expected in the pure subset of a functional language, CameLIGO only
|
As expected in the pure subset of a functional language, CameLIGO only
|
||||||
features constant values: once they are declared, the value cannot be
|
features *constant values*: once they are declared, the value cannot
|
||||||
changed (or "mutated").
|
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.
|
|
||||||
|
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let add (a : int) (b : int) : int =
|
let add (a : int) (b : int) : int =
|
||||||
@ -110,12 +104,9 @@ ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/
|
|||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
As expected in the pure subset of a functional language, ReasonLIGO
|
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").
|
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
|
```reasonligo group=c
|
||||||
let add = ((a, b): (int, int)): int => {
|
let add = ((a, b): (int, int)): int => {
|
||||||
let c : int = a + b;
|
let c : int = a + b;
|
||||||
|
@ -3,68 +3,83 @@ const React = require('react');
|
|||||||
const pre = '```';
|
const pre = '```';
|
||||||
|
|
||||||
const PASCALIGO_EXAMPLE = `${pre}pascaligo
|
const PASCALIGO_EXAMPLE = `${pre}pascaligo
|
||||||
// variant defining pseudo multi-entrypoint actions
|
type storage is int
|
||||||
type action is
|
|
||||||
| Increment of int
|
type parameter is
|
||||||
|
Increment of int
|
||||||
| Decrement 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 add (const store : storage; const delta : int) : storage is store + delta
|
||||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
function sub (const store : storage; const delta : int) : storage is store - delta
|
||||||
((nil : list(operation)),
|
|
||||||
case p of
|
(* Main access point that dispatches to the entrypoints according to
|
||||||
| Increment (n) -> add (s, n)
|
the smart contract parameter. *)
|
||||||
| Decrement (n) -> subtract (s, n)
|
|
||||||
end)
|
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}`;
|
${pre}`;
|
||||||
|
|
||||||
const CAMELIGO_EXAMPLE = `${pre}ocaml
|
const CAMELIGO_EXAMPLE = `${pre}ocaml
|
||||||
type storage = int
|
type storage = int
|
||||||
|
|
||||||
(* variant defining pseudo multi-entrypoint actions *)
|
type parameter =
|
||||||
|
Increment of int
|
||||||
type action =
|
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
| Decrement of int
|
||||||
|
| Reset
|
||||||
|
|
||||||
let add (a,b: int * int) : int = a + b
|
type return = operation list * storage
|
||||||
let sub (a,b: int * int) : int = a - b
|
|
||||||
|
|
||||||
(* real entrypoint that re-routes the flow based on the action provided *)
|
// Two entrypoints
|
||||||
|
|
||||||
let main (p,s: action * storage) =
|
let add (store, delta : storage * int) : storage = store + delta
|
||||||
let storage =
|
let sub (store, delta : storage * int) : storage = store - delta
|
||||||
match p with
|
|
||||||
| Increment n -> add (s, n)
|
(* Main access point that dispatches to the entrypoints according to
|
||||||
| Decrement n -> sub (s, n)
|
the smart contract parameter. *)
|
||||||
in ([] : operation list), storage
|
|
||||||
|
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}`;
|
${pre}`;
|
||||||
|
|
||||||
|
|
||||||
const REASONLIGO_EXAMPLE = `${pre}reasonligo
|
const REASONLIGO_EXAMPLE = `${pre}reasonligo
|
||||||
type storage = int;
|
type storage = int;
|
||||||
|
|
||||||
/* variant defining pseudo multi-entrypoint actions */
|
type parameter =
|
||||||
|
Increment (int)
|
||||||
|
| Decrement (int)
|
||||||
|
| Reset;
|
||||||
|
|
||||||
type action =
|
type return = (list (operation), storage);
|
||||||
| Increment(int)
|
|
||||||
| Decrement(int);
|
|
||||||
|
|
||||||
let add = ((a,b): (int, int)): int => a + b;
|
(* Two entrypoints *)
|
||||||
let sub = ((a,b): (int, int)): int => a - b;
|
|
||||||
|
|
||||||
/* 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)) => {
|
(* Main access point that dispatches to the entrypoints according to
|
||||||
let storage =
|
the smart contract parameter. *)
|
||||||
switch (p) {
|
|
||||||
| Increment(n) => add((storage, n))
|
let main = ((action, store) : (parameter, storage)) : return => {
|
||||||
| Decrement(n) => sub((storage, n))
|
(([] : list (operation)), // No operations
|
||||||
};
|
(switch (action) {
|
||||||
([]: list(operation), storage);
|
| Increment (n) => add ((store, n))
|
||||||
|
| Decrement (n) => sub ((store, n))
|
||||||
|
| Reset => 0}))
|
||||||
};
|
};
|
||||||
${pre}`;
|
${pre}`;
|
||||||
|
|
||||||
|
@ -6,14 +6,14 @@ const docUrl = require(`${process.cwd()}/core/UrlUtils`).docUrl;
|
|||||||
const FEATURES = [
|
const FEATURES = [
|
||||||
{
|
{
|
||||||
image: 'img/strong-type-system.svg',
|
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.'
|
content: 'Write types, then code. Benefit from the safety of type systems.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: 'img/syntax-agnostic.svg',
|
image: 'img/syntax-agnostic.svg',
|
||||||
title: 'Syntax Agnostic',
|
title: 'Polyglot',
|
||||||
content:
|
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',
|
image: 'img/easy-integration.svg',
|
||||||
@ -77,7 +77,7 @@ module.exports = props => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="preview">
|
<div id="preview">
|
||||||
<h1>A friendly smart-contract language for Tezos</h1>
|
<h1>A friendly Smart Contract Language for Tezos</h1>
|
||||||
<p>Michelson was never so easy</p>
|
<p>Michelson was never so easy</p>
|
||||||
<CodeExamples MarkdownBlock={MarkdownBlock}></CodeExamples>
|
<CodeExamples MarkdownBlock={MarkdownBlock}></CodeExamples>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@ let reasonHighlightJs = require('reason-highlightjs');
|
|||||||
|
|
||||||
const siteConfig = {
|
const siteConfig = {
|
||||||
title: 'LIGO', // Title for your website.
|
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',
|
taglineSub: 'Michelson was never so easy',
|
||||||
url: 'https://ligolang.org', // Your website URL
|
url: 'https://ligolang.org', // Your website URL
|
||||||
baseUrl: '/', // Base URL for your project */
|
baseUrl: '/', // Base URL for your project */
|
||||||
@ -14,7 +14,7 @@ const siteConfig = {
|
|||||||
|
|
||||||
// Used for publishing and more
|
// Used for publishing and more
|
||||||
projectName: 'ligo',
|
projectName: 'ligo',
|
||||||
organizationName: 'marigold',
|
organizationName: 'TBN',
|
||||||
// For top-level user or org sites, the organization is still the same.
|
// 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...
|
// e.g., for the https://JoelMarcey.github.io site, it would be set like...
|
||||||
// organizationName: 'JoelMarcey'
|
// organizationName: 'JoelMarcey'
|
||||||
@ -87,10 +87,11 @@ const siteConfig = {
|
|||||||
beginKeywords: '',
|
beginKeywords: '',
|
||||||
keywords: {
|
keywords: {
|
||||||
keyword:
|
keyword:
|
||||||
'and begin block case const contains down else end fail for ' +
|
'and attributes begin big_map block case const contains else'
|
||||||
'from function if in is list map mod nil not of or patch ' +
|
+ ' end False for from function if in is list map mod nil'
|
||||||
'procedure record remove set skip step then to type var while with',
|
+ ' not of or patch record remove set skip then to True type'
|
||||||
literal: 'true false unit int string some none bool nat list'
|
+ ' var while with',
|
||||||
|
literal: 'true false unit int string Some None bool nat list'
|
||||||
},
|
},
|
||||||
lexemes: '[a-zA-Z][a-zA-Z0-9_]*',
|
lexemes: '[a-zA-Z][a-zA-Z0-9_]*',
|
||||||
contains: [
|
contains: [
|
||||||
|
@ -4,8 +4,8 @@ title: Origin
|
|||||||
original_id: 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.
|
> 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.
|
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.
|
@ -4,23 +4,22 @@ title: Philosophy
|
|||||||
original_id: 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
|
## 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.
|
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
|
||||||
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.
|
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
|
||||||
Static analysis is the process of having a program analyze another one.
|
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
|
### 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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -6,15 +6,27 @@
|
|||||||
"version-next-intro/editor-support"
|
"version-next-intro/editor-support"
|
||||||
],
|
],
|
||||||
"Language Basics": [
|
"Language Basics": [
|
||||||
"version-next-language-basics/cheat-sheet",
|
|
||||||
"version-next-language-basics/types",
|
"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/functions",
|
||||||
"version-next-language-basics/entrypoints",
|
"version-next-language-basics/boolean-if-else",
|
||||||
"version-next-language-basics/operators"
|
"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": [
|
"API": [
|
||||||
"version-next-api-cli-commands"
|
"version-next-api/cli-commands",
|
||||||
|
"version-next-api/cheat-sheet"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"version-next-contributors-docs": {
|
"version-next-contributors-docs": {
|
||||||
|
@ -185,6 +185,7 @@ module Simplify = struct
|
|||||||
| "Set.literal" -> ok C_SET_LITERAL
|
| "Set.literal" -> ok C_SET_LITERAL
|
||||||
| "Set.add" -> ok C_SET_ADD
|
| "Set.add" -> ok C_SET_ADD
|
||||||
| "Set.remove" -> ok C_SET_REMOVE
|
| "Set.remove" -> ok C_SET_REMOVE
|
||||||
|
| "Set.iter" -> ok C_SET_ITER
|
||||||
| "Set.fold" -> ok C_SET_FOLD
|
| "Set.fold" -> ok C_SET_FOLD
|
||||||
| "Set.size" -> ok C_SIZE
|
| "Set.size" -> ok C_SIZE
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user