Complete rewrite of the documentation.
This commit is contained in:
parent
d92a5499e1
commit
410bbb0723
@ -1,173 +1,227 @@
|
|||||||
---
|
---
|
||||||
id: entrypoints-contracts
|
id: entrypoints-contracts
|
||||||
title: Entrypoints, Contracts
|
title: Entrypoints to Contracts
|
||||||
---
|
---
|
||||||
|
|
||||||
## Entrypoints
|
## Entrypoints
|
||||||
|
|
||||||
Each LIGO smart contract is essentially a single main function, referring to the following types:
|
A LIGO contract is made of a series of constant and function
|
||||||
|
declarations. Only functions having a special type can be called when
|
||||||
|
the contract is activated: they are called *entrypoints*. An
|
||||||
|
entrypoint need to take two parameters, the *contract parameter* and
|
||||||
|
the *on-chain storage*, and return a pair made of a *list of
|
||||||
|
operations* and a (new) storage.
|
||||||
|
|
||||||
|
When the contract is originated, the initial value of the storage is
|
||||||
|
provided. When and entrypoint is later called, only the parameter is
|
||||||
|
provided, but the type of an entrypoint contains both.
|
||||||
|
|
||||||
|
The type of the contract parameter and the storage are up to the
|
||||||
|
contract designer, but the type for list operations is not. The return
|
||||||
|
type of an entrypoint is as follows, assuming that the type `storage`
|
||||||
|
has been defined elsewhere. (Note that you can use any type with any
|
||||||
|
name for the storage.)
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
|
```pascaligo skip
|
||||||
|
type storage is ... // Any name, any type
|
||||||
|
type return is list (operation) * storage
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo skip
|
||||||
|
type storage = ... // Any name, any type
|
||||||
|
type return = operation list * storage
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo skip
|
||||||
|
type storage = ...; // Any name, any type
|
||||||
|
type return = (list (operation), storage);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
The contract storage can only be modified by activating an
|
||||||
|
entrypoint. It is important to understand what that means. What it
|
||||||
|
does *not* mean is that some global variable holding the storage is
|
||||||
|
modified by the entrypoint. Instead, what it *does* mean is that,
|
||||||
|
given the state of the storage *on-chain*, an entrypoint specifies how
|
||||||
|
to create another state for it, depending on a parameter.
|
||||||
|
|
||||||
|
Here is an example where the storage is a single natural number that
|
||||||
|
is updated by the parameter.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
type parameter_t is unit
|
type storage is nat
|
||||||
type storage_t is unit
|
type return is list (operation) * storage
|
||||||
type return_t is (list(operation) * storage_t)
|
|
||||||
|
function save (const parameter : nat; const store : storage) : return is
|
||||||
|
((nil : list (operation)), parameter)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
type parameter_t = unit
|
type storage = nat
|
||||||
type storage_t = unit
|
|
||||||
type return_t = (operation list * storage_t)
|
let save (parameter, store: nat * storage) : return =
|
||||||
|
(([] : operation list), parameter)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
type parameter_t = unit;
|
type storage = nat;
|
||||||
type storage_t = unit;
|
|
||||||
type return_t = (list(operation) , storage_t);
|
|
||||||
```
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
Each main function receives two arguments:
|
let main = ((parameter, store): (nat, storage)) : return => {
|
||||||
- `parameter` - this is the parameter received in the invocation operation
|
(([] : list (operation)), parameter);
|
||||||
- `storage` - this is the current (real) on-chain storage value
|
|
||||||
|
|
||||||
Storage can only be modified by running the smart contract entrypoint, which is responsible for returning a pair holding a list of operations, and a new storage.
|
|
||||||
|
|
||||||
Here is an example of a smart contract main function:
|
|
||||||
|
|
||||||
> 💡 The contract below literally does *nothing*
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
function main(const parameter: parameter_t; const store: storage_t): return_t is
|
|
||||||
((nil : list(operation)), store)
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=a
|
|
||||||
let main (parameter, store: parameter_t * storage_t) : return_t =
|
|
||||||
(([]: operation list), store)
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=a
|
|
||||||
let main = ((parameter, store): (parameter_t, storage_t)) : return_t => {
|
|
||||||
(([]: list(operation)), store);
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
A contract entrypoints are the constructors of the parameter type (variant) and you must use pattern matching (`case`, `match`, `switch`) on the parameter in order to associate each entrypoint to its corresponding handler.
|
In LIGO, the design pattern for entrypoints consists in actually
|
||||||
|
having exactly *one entrypoint*, like the `main` function in C. The
|
||||||
|
parameter of the contract is then a variant type, and, depending on
|
||||||
|
the constructors of that type, different functions in the contract are
|
||||||
|
called. In other terms, the unique entrypoint dispatches the control
|
||||||
|
flow depending on a *pattern matching* on the contract parameter.
|
||||||
|
|
||||||
To access the 'entrypoints' of a contract, we define a main function whose parameter is a variant type with constructors for each entrypoint. This allows us to satisfy the requirement that LIGO contracts always begin execution from the same function. The main function simply takes this variant, pattern matches it to determine which entrypoint to dispatch the call to, then returns the result of executing that entrypoint with the projected arguments.
|
In the following example, the storage contains a counter (of type
|
||||||
|
`nat`) and a name (of type `string`). Depending on the parameter of
|
||||||
> The LIGO variant's are compiled to a Michelson annotated tree of union type.
|
the contract, either the counter or the name is updated.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=recordentry
|
|
||||||
type parameter_t is
|
|
||||||
| Entrypoint_a of int
|
|
||||||
| Entrypoint_b of string
|
|
||||||
type storage_t is unit
|
|
||||||
type return_t is (list(operation) * storage_t)
|
|
||||||
|
|
||||||
function handle_a (const p : int; const store : storage_t) : return_t is
|
|
||||||
((nil : list(operation)), store)
|
|
||||||
|
|
||||||
function handle_b (const p : string; const store : storage_t) : return_t is
|
|
||||||
((nil : list(operation)), store)
|
|
||||||
|
|
||||||
function main(const parameter: parameter_t; const store: storage_t): return_t is
|
|
||||||
case parameter of
|
|
||||||
| Entrypoint_a (p) -> handle_a(p,store)
|
|
||||||
| Entrypoint_b (p) -> handle_b(p,store)
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=recordentry
|
|
||||||
type parameter_t =
|
|
||||||
| Entrypoint_a of int
|
|
||||||
| Entrypoint_b of string
|
|
||||||
type storage_t = unit
|
|
||||||
type return_t = (operation list * storage_t)
|
|
||||||
|
|
||||||
let handle_a (parameter, store: int * storage_t) : return_t =
|
|
||||||
(([]: operation list), store)
|
|
||||||
|
|
||||||
let handle_b (parameter, store: string * storage_t) : return_t =
|
|
||||||
(([]: operation list), store)
|
|
||||||
|
|
||||||
let main (parameter, store: parameter_t * storage_t) : return_t =
|
|
||||||
match parameter with
|
|
||||||
| Entrypoint_a p -> handle_a (p,store)
|
|
||||||
| Entrypoint_b p -> handle_b (p,store)
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=recordentry
|
|
||||||
type parameter_t =
|
|
||||||
| Entrypoint_a(int)
|
|
||||||
| Entrypoint_b(string);
|
|
||||||
type storage_t = unit;
|
|
||||||
type return_t = (list(operation) , storage_t);
|
|
||||||
|
|
||||||
let handle_a = ((parameter, store): (int, storage_t)) : return_t => {
|
|
||||||
(([]: list(operation)), store); };
|
|
||||||
|
|
||||||
let handle_b = ((parameter, store): (string, storage_t)) : return_t => {
|
|
||||||
(([]: list(operation)), store); };
|
|
||||||
|
|
||||||
let main = ((parameter, store): (parameter_t, storage_t)) : return_t => {
|
|
||||||
switch (parameter) {
|
|
||||||
| Entrypoint_a(p) => handle_a((p,store))
|
|
||||||
| Entrypoint_b(p) => handle_b((p,store))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
|
||||||
## Built-in contract variables
|
|
||||||
|
|
||||||
Each LIGO smart contract deployed on the Tezos blockchain, has access to certain built-in variables/constants that can be used to determine a range
|
|
||||||
of useful things. In this section you'll find how those built-ins can be utilized.
|
|
||||||
|
|
||||||
### Accepting/declining money in a smart contract
|
|
||||||
|
|
||||||
This example shows how `amount` and `failwith` can be used to decline a transaction that sends more tez than `0mutez`.
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
type parameter is
|
||||||
block {
|
Entrypoint_A of nat
|
||||||
if amount > 0mutez then failwith("This contract does not accept tez") else skip
|
| Entrypoint_B of string
|
||||||
} with ((nil : list(operation)), unit);
|
|
||||||
|
type storage is record [
|
||||||
|
counter : nat;
|
||||||
|
name : string
|
||||||
|
]
|
||||||
|
|
||||||
|
type return is list (operation) * storage
|
||||||
|
|
||||||
|
function handle_A (const n : nat; const store : storage) : return is
|
||||||
|
((nil : list (operation)), store with record [counter = n])
|
||||||
|
|
||||||
|
function handle_B (const s : string; const store : storage) : return is
|
||||||
|
((nil : list (operation)), store with record [name = s])
|
||||||
|
|
||||||
|
function main (const param : parameter; const store : storage): return is
|
||||||
|
case param of
|
||||||
|
Entrypoint_A (n) -> handle_A (n, store)
|
||||||
|
| Entrypoint_B (s) -> handle_B (s, store)
|
||||||
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=b
|
```cameligo group=b
|
||||||
let main (p, s: unit * unit) : operation list * unit =
|
type parameter =
|
||||||
if amount > 0mutez
|
Entrypoint_A of nat
|
||||||
then (failwith "This contract does not accept tez": operation list * unit)
|
| Entrypoint_B of string
|
||||||
else (([]: operation list), unit)
|
|
||||||
|
type storage = {
|
||||||
|
counter : nat;
|
||||||
|
name : string
|
||||||
|
}
|
||||||
|
|
||||||
|
type return = operation list * storage
|
||||||
|
|
||||||
|
let handle_A (n, store : nat * storage) : return =
|
||||||
|
([] : operation list), {store with counter = n}
|
||||||
|
|
||||||
|
let handle_B (s, store : string * storage) : return =
|
||||||
|
([] : operation list), {store with name = s}
|
||||||
|
|
||||||
|
let main (param, store: parameter * storage) : return =
|
||||||
|
match param with
|
||||||
|
Entrypoint_A n -> handle_A (n, store)
|
||||||
|
| Entrypoint_B s -> handle_B (s, store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
let main = ((p,s): (unit, unit)) : (list(operation), unit) => {
|
type parameter =
|
||||||
if (amount > 0mutez) {
|
| Entrypoint_A (nat)
|
||||||
(failwith("This contract does not accept tez"): (list(operation), unit));
|
| Entrypoint_B (string);
|
||||||
|
|
||||||
|
type storage = {
|
||||||
|
counter : nat,
|
||||||
|
name : string
|
||||||
|
};
|
||||||
|
|
||||||
|
type return = (list (operation), storage);
|
||||||
|
|
||||||
|
let handle_A = ((n, store): (nat, storage)) : return => {
|
||||||
|
(([] : list (operation)), {...store, counter : n}); };
|
||||||
|
|
||||||
|
let handle_B = ((s, store): (string, storage)) : return => {
|
||||||
|
(([] : list (operation)), {...store, name : s}); };
|
||||||
|
|
||||||
|
let main = ((param, store): (parameter, storage)) : return => {
|
||||||
|
switch (param) {
|
||||||
|
| Entrypoint_A (n) => handle_A ((n, store))
|
||||||
|
| Entrypoint_B (s) => handle_B ((s, store))
|
||||||
}
|
}
|
||||||
else {
|
};
|
||||||
(([]: list(operation)), ());
|
```
|
||||||
};
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
## Tezos-specific Built-ins
|
||||||
|
|
||||||
|
A LIGO smart contract can query part of the state of the Tezos
|
||||||
|
blockchain by means of built-in values. In this section you will find
|
||||||
|
how those built-ins can be utilized.
|
||||||
|
|
||||||
|
### Accepting or Declining Tokens in a Smart Contract
|
||||||
|
|
||||||
|
This example shows how `amount` and `failwith` can be used to decline
|
||||||
|
any transaction that sends more tez than `0mutez`, that is, no
|
||||||
|
incoming tokens are accepted.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=c
|
||||||
|
type parameter is unit
|
||||||
|
type storage is unit
|
||||||
|
type return is list (operation) * storage
|
||||||
|
|
||||||
|
function deny (const param : parameter; const store : storage) : return is
|
||||||
|
if amount > 0mutez then
|
||||||
|
(failwith ("This contract does not accept tokens.") : return)
|
||||||
|
else ((nil : list (operation)), store)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=c
|
||||||
|
type parameter = unit
|
||||||
|
type storage = unit
|
||||||
|
type return = operation list * storage
|
||||||
|
|
||||||
|
let deny (param, store : parameter * storage) : return =
|
||||||
|
if amount > 0mutez then
|
||||||
|
(failwith "This contract does not accept tokens.": return)
|
||||||
|
else (([] : operation list), store)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=c
|
||||||
|
type parameter = unit;
|
||||||
|
type storage = unit;
|
||||||
|
type return = (list (operation), storage);
|
||||||
|
|
||||||
|
let deny = ((param, store): (parameter, storage)) : return => {
|
||||||
|
if (amount > 0mutez) {
|
||||||
|
(failwith("This contract does not accept tokens."): return); }
|
||||||
|
else { (([] : list (operation)), store); };
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -180,128 +234,165 @@ This example shows how `sender` or `source` can be used to deny access to an ent
|
|||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
||||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
|
||||||
block {
|
function filter (const param : parameter; const store : storage) : return is
|
||||||
if source =/= owner then failwith("This address can't call the contract") else skip
|
if source =/= owner then (failwith ("Access denied.") : return)
|
||||||
} with ((nil : list(operation)), unit);
|
else ((nil : list(operation)), store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)
|
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)
|
||||||
let main (p,s: unit * unit) : operation list * unit =
|
|
||||||
if source <> owner
|
let filter (param, store: parameter * storage) : return =
|
||||||
then (failwith "This address can't call the contract": operation list * unit)
|
if source <> owner then (failwith "Access denied." : return)
|
||||||
else (([]: operation list), ())
|
else (([] : operation list), store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
||||||
let main = ((p,s): (unit, unit)) : (list(operation), unit) => {
|
|
||||||
if (source != owner) {
|
let main = ((param, store): (parameter, storage)) : storage => {
|
||||||
(failwith("This address can't call the contract"): (list(operation), unit));
|
if (source != owner) { (failwith ("Access denied.") : return); }
|
||||||
}
|
else { (([] : list (operation)), store); };
|
||||||
else {
|
|
||||||
(([]: list(operation)), ());
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
### Cross contract calls
|
### Inter-Contract Invocations
|
||||||
|
|
||||||
This example shows how a contract can invoke another contract by emiting a transaction operation at the end of an entrypoint.
|
It would be somewhat misleading to speak of "contract calls", as this
|
||||||
|
wording may wrongly suggest an analogy between contract "calls" and
|
||||||
|
function "calls". Indeed, the control flow returns to the site of a
|
||||||
|
function call, and composed function calls therefore are *stacked*,
|
||||||
|
that is, they follow a last in, first out ordering. This is not what
|
||||||
|
happens when a contract invokes another: the invocation is *queued*,
|
||||||
|
that is, follows a first in, first our ordering, and the dequeuing
|
||||||
|
only starts at the normal end of a contract (no failure). That is why
|
||||||
|
we speak of "contract invocations" instead of "calls".
|
||||||
|
|
||||||
> The same technique can be used to transfer tez to an implicit account (tz1, ...), all you have to do is use `unit` instead of a parameter for a smart contract.
|
The following example shows how a contract can invoke another by
|
||||||
|
emiting a transaction operation at the end of an entrypoint.
|
||||||
|
|
||||||
In our case, we have a `counter.ligo` contract that accepts a parameter of type `action`, and we have a `proxy.ligo` contract that accepts the same parameter type, and forwards the call to the deployed counter contract.
|
> The same technique can be used to transfer tokens to an implicit
|
||||||
|
> account (tz1, ...): all you have to do is use a unit value as the
|
||||||
|
> parameter of the smart contract.
|
||||||
|
|
||||||
|
In our case, we have a `counter.ligo` contract that accepts a
|
||||||
|
parameter of type `action`, and we have a `proxy.ligo` contract that
|
||||||
|
accepts the same parameter type, and forwards the call to the deployed
|
||||||
|
counter contract.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo skip
|
```pascaligo skip
|
||||||
// counter.ligo
|
// counter.ligo
|
||||||
type action is
|
type parameter is
|
||||||
| Increment of int
|
Increment of nat
|
||||||
| Decrement of int
|
| Decrement of nat
|
||||||
| Reset of unit
|
| Reset
|
||||||
|
|
||||||
|
type storage is unit
|
||||||
|
|
||||||
|
type return is list (operation) * storage
|
||||||
```
|
```
|
||||||
|
|
||||||
```pascaligo skip
|
```pascaligo group=d
|
||||||
// proxy.ligo
|
// proxy.ligo
|
||||||
|
|
||||||
type action is
|
type parameter is
|
||||||
| Increment of int
|
Increment of nat
|
||||||
| Decrement of int
|
| Decrement of nat
|
||||||
| Reset of unit
|
| Reset
|
||||||
|
|
||||||
const dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address);
|
type storage is unit
|
||||||
|
|
||||||
function proxy(const param: action; const store: unit): (list(operation) * unit)
|
type return is list (operation) * storage
|
||||||
is block {
|
|
||||||
const counter: contract(action) = get_contract(dest);
|
const dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address)
|
||||||
// re-use the param passed to the proxy in the subsequent transaction
|
|
||||||
// e.g.:
|
function proxy (const param : parameter; const store : storage): return is
|
||||||
// const mockParam: action = Increment(5);
|
block {
|
||||||
const op: operation = transaction(param, 0mutez, counter);
|
const counter : contract (parameter) = get_contract (dest);
|
||||||
const opList: list(operation) = list op; end;
|
(* Reuse the parameter in the subsequent
|
||||||
} with (opList, store)
|
transaction or use another one, `mock_param`. *)
|
||||||
|
const mock_param : parameter = Increment (5n);
|
||||||
|
const op : operation = transaction (param, 0mutez, counter);
|
||||||
|
const ops : list (operation) = list [op]
|
||||||
|
} with (ops, store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo skip
|
||||||
// counter.mligo
|
// counter.mligo
|
||||||
type action =
|
|
||||||
| Increment of int
|
type paramater =
|
||||||
| Decrement of int
|
Increment of nat
|
||||||
| Reset of unit
|
| Decrement of nat
|
||||||
|
| Reset
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```cameligo
|
```cameligo group=d
|
||||||
// proxy.mligo
|
// proxy.mligo
|
||||||
|
|
||||||
type action =
|
type parameter =
|
||||||
| Increment of int
|
Increment of nat
|
||||||
| Decrement of int
|
| Decrement of nat
|
||||||
| Reset of unit
|
| Reset
|
||||||
|
|
||||||
let dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address)
|
type storage = unit
|
||||||
|
|
||||||
let proxy (param, storage: action * unit): operation list * unit =
|
type return = operation list * storage
|
||||||
let counter: action contract = Operation.get_contract dest in
|
|
||||||
let op: operation = Operation.transaction param 0mutez counter in
|
let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address)
|
||||||
[op], storage
|
|
||||||
|
let proxy (param, store : parameter * storage) : return =
|
||||||
|
let counter : parameter contract = Operation.get_contract dest in
|
||||||
|
(* Reuse the parameter in the subsequent
|
||||||
|
transaction or use another one, `mock_param`. *)
|
||||||
|
let mock_param : parameter = Increment (5n) in
|
||||||
|
let op : operation = Operation.transaction param 0mutez counter
|
||||||
|
in [op], store
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo skip
|
||||||
// counter.religo
|
// counter.religo
|
||||||
|
|
||||||
type action =
|
type parameter =
|
||||||
| Increment(int)
|
| Increment (nat)
|
||||||
| Decrement(int)
|
| Decrement (nat)
|
||||||
| Reset(unit);
|
| Reset
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```reasonligo
|
```reasonligo group=d
|
||||||
// proxy.religo
|
// proxy.religo
|
||||||
|
|
||||||
type action =
|
type parameter =
|
||||||
| Increment(int)
|
| Increment (nat)
|
||||||
| Decrement(int)
|
| Decrement (nat)
|
||||||
| Reset(unit);
|
| Reset;
|
||||||
|
|
||||||
let dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address);
|
type storage = unit;
|
||||||
|
|
||||||
let proxy = ((param, s): (action, unit)): (list(operation), unit) =>
|
type return = (list (operation), storage);
|
||||||
let counter: contract(action) = Operation.get_contract(dest);
|
|
||||||
let op: operation = Operation.transaction(param, 0mutez, counter);
|
let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address);
|
||||||
([op], s);
|
|
||||||
|
let proxy = ((param, store): (parameter, storage)) : return =>
|
||||||
|
let counter : contract (parameter) = Operation.get_contract (dest);
|
||||||
|
(* Reuse the parameter in the subsequent
|
||||||
|
transaction or use another one, `mock_param`. *)
|
||||||
|
let mock_param : parameter = Increment (5n);
|
||||||
|
let op : operation = Operation.transaction (param, 0mutez, counter);
|
||||||
|
([op], store);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
|
||||||
block {
|
|
||||||
if amount > 0mutez then failwith("This contract does not accept tez") else skip
|
|
||||||
} with ((nil : list(operation)), unit);
|
|
@ -1,5 +0,0 @@
|
|||||||
const owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
|
||||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
|
||||||
block {
|
|
||||||
if source =/= owner then failwith("This address can't call the contract") else skip
|
|
||||||
} with ((nil : list(operation)), unit);
|
|
@ -1,9 +0,0 @@
|
|||||||
#include "counter.types.ligo"
|
|
||||||
|
|
||||||
function counter (const p : action ; const s : int): (list(operation) * int) is
|
|
||||||
block { skip } with ((nil : list(operation)),
|
|
||||||
case p of
|
|
||||||
| Increment(n) -> s + n
|
|
||||||
| Decrement(n) -> s - n
|
|
||||||
| Reset(n) -> 0
|
|
||||||
end)
|
|
@ -1,4 +0,0 @@
|
|||||||
type action is
|
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
|
||||||
| Reset of unit
|
|
@ -1,14 +0,0 @@
|
|||||||
#include "counter.types.ligo"
|
|
||||||
|
|
||||||
// Replace the following address with your deployed counter contract address
|
|
||||||
const address: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address);
|
|
||||||
|
|
||||||
function proxy(const param: action; const store: unit): (list(operation) * unit)
|
|
||||||
is block {
|
|
||||||
const counter: contract(action) = get_contract(address);
|
|
||||||
// re-use the param passed to the proxy in the subsequent transaction
|
|
||||||
// e.g.:
|
|
||||||
// const mockParam: action = Increment(5);
|
|
||||||
const op: operation = transaction(param, 0mutez, counter);
|
|
||||||
const opList: list(operation) = list op; end;
|
|
||||||
} with (opList, store)
|
|
@ -1,9 +0,0 @@
|
|||||||
const today: timestamp = now;
|
|
||||||
const one_day: int = 86400;
|
|
||||||
const in_24_hrs: timestamp = today + one_day;
|
|
||||||
|
|
||||||
const today: timestamp = now;
|
|
||||||
const one_day: int = 86400;
|
|
||||||
const a_24_hrs_ago: timestamp = today - one_day;
|
|
||||||
|
|
||||||
const not_tommorow: bool = (now = in_24_hrs)
|
|
@ -3,179 +3,204 @@ id: timestamps-addresses
|
|||||||
title: Timestamps, Addresses
|
title: Timestamps, Addresses
|
||||||
---
|
---
|
||||||
|
|
||||||
## Timestamps
|
## Timestamps
|
||||||
|
|
||||||
Timestamps in LIGO, or in Michelson in general are available in smart contracts, while bakers baking the block (including the transaction in a block) are responsible for providing the given current timestamp for the contract.
|
LIGO features timestamps, as Michelson does, while bakers baking the
|
||||||
|
block (including the transaction in a block) are responsible for
|
||||||
|
providing the given current timestamp for the contract.
|
||||||
|
|
||||||
### Current time
|
### Current Time
|
||||||
|
|
||||||
You can obtain the current time using the built-in syntax specific expression, please be aware that it's up to the baker to set the current timestamp value.
|
|
||||||
|
|
||||||
|
You can obtain the current time using the built-in syntax specific
|
||||||
|
expression, please be aware that it is up to the baker to set the
|
||||||
|
current timestamp value.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const today: timestamp = now;
|
const today : timestamp = now
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
let today: timestamp = Current.time
|
let today : timestamp = Current.time
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
let today: timestamp = Current.time;
|
let today : timestamp = Current.time;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
> When running code with ligo CLI, the option `--predecessor-timestamp` allows you to control what `now` returns.
|
> When running code with ligo CLI, the option
|
||||||
|
> `--predecessor-timestamp` allows you to control what `now` returns.
|
||||||
|
|
||||||
### Timestamp arithmetic
|
### Timestamp Arithmetic
|
||||||
|
|
||||||
In LIGO, timestamps can be added with `int`(s), this enables you to set e.g. time constraints for your smart contracts like this:
|
In LIGO, timestamps can be added to integers, allowing you to set time
|
||||||
|
constraints on your smart contracts. Consider the following scenarios.
|
||||||
|
|
||||||
#### In 24 hours
|
#### 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
|
||||||
const in_24_hrs: timestamp = today + one_day;
|
const in_24_hrs : timestamp = today + one_day
|
||||||
const some_date: timestamp = ("2000-01-01T10:10:10Z" : timestamp);
|
const some_date : timestamp = ("2000-01-01T10:10:10Z" : timestamp)
|
||||||
const one_day_later: timestamp = some_date + one_day;
|
const one_day_later : timestamp = some_date + one_day
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=b
|
```cameligo group=b
|
||||||
let today: timestamp = Current.time
|
let today : timestamp = Current.time
|
||||||
let one_day: int = 86400
|
let one_day : int = 86400
|
||||||
let in_24_hrs: timestamp = today + one_day
|
let in_24_hrs : timestamp = today + one_day
|
||||||
let some_date: timestamp = ("2000-01-01t10:10:10Z" : timestamp)
|
let some_date : timestamp = ("2000-01-01t10:10:10Z" : timestamp)
|
||||||
let one_day_later: timestamp = some_date + one_day
|
let one_day_later : timestamp = some_date + one_day
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
let today: timestamp = Current.time;
|
let today : timestamp = Current.time;
|
||||||
let one_day: int = 86400;
|
let one_day : int = 86400;
|
||||||
let in_24_hrs: timestamp = today + one_day;
|
let in_24_hrs : timestamp = today + one_day;
|
||||||
let some_date: timestamp = ("2000-01-01t10:10:10Z" : timestamp);
|
let some_date : timestamp = ("2000-01-01t10:10:10Z" : timestamp);
|
||||||
let one_day_later: timestamp = some_date + one_day;
|
let one_day_later : timestamp = some_date + one_day;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
#### 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
|
||||||
const in_24_hrs: timestamp = today - one_day;
|
const in_24_hrs : timestamp = today - one_day
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let today: timestamp = Current.time
|
let today : timestamp = Current.time
|
||||||
let one_day: int = 86400
|
let one_day : int = 86400
|
||||||
let in_24_hrs: timestamp = today - one_day
|
let in_24_hrs : timestamp = today - one_day
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let today: timestamp = Current.time;
|
let today : timestamp = Current.time;
|
||||||
let one_day: int = 86400;
|
let one_day : int = 86400;
|
||||||
let in_24_hrs: timestamp = today - one_day;
|
let in_24_hrs : timestamp = today - one_day;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
### Comparing timestamps
|
### Comparing Timestamps
|
||||||
|
|
||||||
You can also compare timestamps using the same comparison operators as for numbers:
|
You can also compare timestamps using the same comparison operators as
|
||||||
|
for numbers.
|
||||||
|
|
||||||
<!--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)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let not_tomorrow: bool = (Current.time = in_24_hrs)
|
let not_tomorrow : bool = (Current.time = in_24_hrs)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let not_tomorrow: bool = (Current.time == in_24_hrs);
|
let not_tomorrow : bool = (Current.time == in_24_hrs);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Addresses
|
## Addresses
|
||||||
|
|
||||||
`address` is a LIGO datatype used for Tezos addresses (tz1, tz2, tz3, KT1, ...).
|
The type `address` in LIGO is used to denote Tezos addresses (tz1,
|
||||||
|
tz2, tz3, KT1, ...). Currently, addresses are created by casting a
|
||||||
Here's how you can define an address:
|
string to the type `address`. Beware of failures if the address is
|
||||||
|
invalid. Consider the following examples.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=d
|
```pascaligo group=d
|
||||||
const my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
const my_account : address =
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=d
|
```cameligo group=d
|
||||||
let my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)
|
let my_account : address =
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=d
|
```reasonligo group=d
|
||||||
let my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
let my_account : address =
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Signatures
|
## Signatures
|
||||||
|
|
||||||
`signature` is a LIGO datatype used for Tezos signature (edsig, spsig).
|
The type `signature` in LIGO datatype is used for Tezos signature
|
||||||
|
(edsig, spsig). Signatures are created by casting a string. Beware of
|
||||||
|
failure if the signature is invalid.
|
||||||
|
|
||||||
Here's how you can define a signature:
|
Here is how you can define a signature:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=e
|
```pascaligo group=e
|
||||||
const my_signature: signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7": signature);
|
const my_sig : signature =
|
||||||
|
("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" :
|
||||||
|
signature)
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=e
|
```cameligo group=e
|
||||||
let my_signature: signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7": signature)
|
let my_sig : signature =
|
||||||
|
("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" :
|
||||||
|
signature)
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=e
|
```reasonligo group=e
|
||||||
let my_signature: signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7": signature);
|
let my_sig : signature =
|
||||||
|
("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" :
|
||||||
|
signature);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## keys
|
## Keys
|
||||||
|
|
||||||
`key` is a LIGO datatype used for Tezos public key.
|
The type `key` in LIGO is used for Tezos public keys. Do not confuse
|
||||||
|
them with map keys. Keys are made by casting strings. Beware of
|
||||||
|
failure if the key is invalid.
|
||||||
|
|
||||||
Here's how you can define a key:
|
Here is how you can define a key.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=f
|
```pascaligo group=f
|
||||||
const my_key: key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav": key);
|
const my_key : key =
|
||||||
|
("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key)
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=f
|
```cameligo group=f
|
||||||
let my_key: key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav": key)
|
let my_key : key =
|
||||||
|
("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key)
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=f
|
```reasonligo group=f
|
||||||
let my_key: key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav": key);
|
let my_key : key =
|
||||||
|
("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -1,61 +1,61 @@
|
|||||||
---
|
---
|
||||||
id: boolean-if-else
|
id: boolean-if-else
|
||||||
title: Boolean, If, Else
|
title: Booleans and Conditionals
|
||||||
---
|
---
|
||||||
|
|
||||||
## Boolean
|
## Booleans
|
||||||
|
|
||||||
The type of a Boolean is `bool` and the possible values are `True` and `False`.
|
The type of a boolean value is `bool`. Here is how to define a boolean
|
||||||
|
value:
|
||||||
Here's how to define a boolean:
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const a: bool = True;
|
const a : bool = True // Notice the capital letter
|
||||||
const b: bool = False;
|
const b : bool = False // Same.
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
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;
|
||||||
let b: bool = false;
|
let b : bool = false;
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Comparing two values
|
## Comparing two Values
|
||||||
|
|
||||||
In LIGO, only values of the same type can be compared. We call these "comparable types." Comparable types include e.g. `int`, `nat`, `string`, `tez`, `timestamp`, `address`, ...
|
In LIGO, only values of the same type can be compared. Moreover, not
|
||||||
|
all values of the same type can be compared, only those with
|
||||||
|
*comparable types*, which is a concept lifted from
|
||||||
|
Michelson. Comparable types include, for instance, `int`, `nat`,
|
||||||
|
`string`, `tez`, `timestamp`, `address`, etc.
|
||||||
|
|
||||||
### Comparing strings
|
### Comparing Strings
|
||||||
|
|
||||||
<!--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"
|
||||||
// True
|
const c : bool = (a = b) // True
|
||||||
const c: bool = (a = b);
|
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=b
|
```cameligo group=b
|
||||||
let a: string = "Alice"
|
let a : string = "Alice"
|
||||||
let b: string = "Alice"
|
let b : string = "Alice"
|
||||||
// true
|
let c : bool = (a = b) // true
|
||||||
let c: bool = (a = b)
|
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
let a: string = "Alice";
|
let a : string = "Alice";
|
||||||
let b: string = "Alice";
|
let b : string = "Alice";
|
||||||
(* true *)
|
let c : bool = (a == b); // true
|
||||||
let c: bool = (a == b);
|
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
@ -65,128 +65,120 @@ let c: bool = (a == b);
|
|||||||
<!--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
|
||||||
const c: bool = (a = b);
|
const c : bool = (a = b)
|
||||||
const d: bool = (a > b);
|
const d : bool = (a > b)
|
||||||
const e: bool = (a < b);
|
const e : bool = (a < b)
|
||||||
const f: bool = (a <= b);
|
const f : bool = (a <= b)
|
||||||
const g: bool = (a >= b);
|
const g : bool = (a >= b)
|
||||||
const h: bool = (a =/= b);
|
const h : bool = (a =/= b)
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let a: int = 5
|
let a : int = 5
|
||||||
let b: int = 4
|
let b : int = 4
|
||||||
let c: bool = (a = b)
|
let c : bool = (a = b)
|
||||||
let d: bool = (a > b)
|
let d : bool = (a > b)
|
||||||
let e: bool = (a < b)
|
let e : bool = (a < b)
|
||||||
let f: bool = (a <= b)
|
let f : bool = (a <= b)
|
||||||
let g: bool = (a >= b)
|
let g : bool = (a >= b)
|
||||||
let h: bool = (a <> b)
|
let h : bool = (a <> b)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let a: int = 5;
|
let a : int = 5;
|
||||||
let b: int = 4;
|
let b : int = 4;
|
||||||
let c: bool = (a == b);
|
let c : bool = (a == b);
|
||||||
let d: bool = (a > b);
|
let d : bool = (a > b);
|
||||||
let e: bool = (a < b);
|
let e : bool = (a < b);
|
||||||
let f: bool = (a <= b);
|
let f : bool = (a <= b);
|
||||||
let g: bool = (a >= b);
|
let g : bool = (a >= b);
|
||||||
let h: bool = (a != b);
|
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 `amount` sent in a transaction.
|
> 💡 Comparing `tez` values is especially useful when dealing with an
|
||||||
|
> 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
|
||||||
const c: bool = (a = b);
|
const c : bool = (a = b) // false
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=d
|
```cameligo group=d
|
||||||
let a: tez = 5mutez
|
let a : tez = 5mutez
|
||||||
let b: tez = 10mutez
|
let b : tez = 10mutez
|
||||||
// false
|
let c : bool = (a = b) // false
|
||||||
let c: bool = (a = b)
|
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=d
|
```reasonligo group=d
|
||||||
let a: tez = 5mutez;
|
let a : tez = 5mutez;
|
||||||
let b: tez = 10mutez;
|
let b : tez = 10mutez;
|
||||||
(* false *)
|
let c : bool = (a == b); // false
|
||||||
let c: bool = (a == b);
|
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Conditionals, if staments, and more
|
## Conditionals
|
||||||
|
|
||||||
Conditional logic is an important part of every real world program.
|
Conditional logic enables to fork the control flow depending on the
|
||||||
|
state.
|
||||||
### If/else statements
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=e
|
```pascaligo group=e
|
||||||
const min_age: nat = 16n;
|
type magnitude is Small | Large // See variant types.
|
||||||
|
|
||||||
function is_adult(const age: nat): bool is
|
function compare (const n : nat) : magnitude is
|
||||||
if (age > min_age) then True else False
|
if n < 10n then Small (Unit) else Large (Unit) // Unit is needed for now.
|
||||||
```
|
```
|
||||||
|
|
||||||
> You can run the function above with
|
You can run the `compare` function defined above using the LIGO compiler
|
||||||
> ```
|
like this:
|
||||||
> ligo run-function -s pascaligo src/if-else.ligo is_adult 21n
|
```shell
|
||||||
> ```
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/boolean-if-else/cond.ligo compare 21n'
|
||||||
|
# Outputs: Large (Unit)
|
||||||
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=e
|
```cameligo group=e
|
||||||
let min_age: nat = 16n
|
type magnitude = Small | Large // See variant types.
|
||||||
|
|
||||||
(**
|
let compare (n : nat) : magnitude =
|
||||||
|
if n < 10n then Small else Large
|
||||||
This function is really obnoxious, but it showcases
|
|
||||||
how the if statement and it's syntax can be used.
|
|
||||||
|
|
||||||
Normally, you'd use `with (age > min_age)` instead.
|
|
||||||
|
|
||||||
*)
|
|
||||||
let is_adult (age: nat) : bool =
|
|
||||||
if (age > min_age) then true else false
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can run the `compare` function defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/boolean-if-else/cond.mligo compare 21n'
|
||||||
|
# Outputs: Large
|
||||||
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=e
|
```reasonligo group=e
|
||||||
let min_age: nat = 16n;
|
type magnitude = | Small | Large; // See variant types.
|
||||||
|
|
||||||
(**
|
let compare = (n : nat) : magnitude =>
|
||||||
|
if (n < 10n) { Small; } else { Large; };
|
||||||
This function is really obnoxious, but it showcases
|
|
||||||
how the if statement and it's syntax can be used.
|
|
||||||
|
|
||||||
Normally, you'd use `with (age > min_age)` instead.
|
|
||||||
|
|
||||||
*)
|
|
||||||
|
|
||||||
let is_adult = (age: nat): bool =>
|
|
||||||
if (age > min_age) {
|
|
||||||
true;
|
|
||||||
} else {
|
|
||||||
false;
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> You can run the function above with
|
You can run the `compare` function defined above using the LIGO compiler
|
||||||
> ```
|
like this:
|
||||||
> ligo run-function -s reasonligo src/if-else.religo is_adult 21n
|
```shell
|
||||||
> ```
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/boolean-if-else/cond.religo compare 21n'
|
||||||
|
# Outputs: Large
|
||||||
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,24 +3,40 @@ id: functions
|
|||||||
title: Functions
|
title: Functions
|
||||||
---
|
---
|
||||||
|
|
||||||
Writing code is fun as long as it doesn't get out of hand. To make sure our code doesn't turn into spaghetti we can group some logic into functions.
|
Writing code is fun as long as it does not get out of hand. To make
|
||||||
|
sure our code does not turn into spaghetti, we can structure some
|
||||||
|
logic into functions.
|
||||||
|
|
||||||
## Instruction blocks
|
## Blocks
|
||||||
|
|
||||||
With `block`(s) you can wrap *instructions* and *expressions* into an isolated scope.
|
In PascaLIGO, *blocks* enable the sequential composition of
|
||||||
Each `block` needs to include at least one `instruction`, or a *placeholder* instruction called `skip`.
|
*instructions* into an isolated scope. Each `block` needs to include
|
||||||
|
at least one instruction. If we need a placeholder *placeholder*, we
|
||||||
|
use the instruction called `skip`, that leaves the state
|
||||||
|
invariant. The rationale for `skip` instead of a truly empty block is
|
||||||
|
that it prevents you from writing an empty block by mistake.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
|
|
||||||
```pascaligo skip
|
```pascaligo skip
|
||||||
// shorthand syntax
|
// terse style
|
||||||
block { a := a + 1 }
|
block { a := a + 1 }
|
||||||
// verbose syntax
|
// verbose style
|
||||||
begin
|
begin
|
||||||
a := a + 1
|
a := a + 1
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
Blocks are more versatile than simply containing instructions:
|
||||||
|
they can also include *declarations* of values, like so:
|
||||||
|
```pascaligo skip
|
||||||
|
// terse style
|
||||||
|
block { const a : int = 1; }
|
||||||
|
// verbose style
|
||||||
|
begin
|
||||||
|
const a : int = 1;
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
@ -32,54 +48,71 @@ end
|
|||||||
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.
|
||||||
|
|
||||||
Here's how you define a basic function that accepts two `int`s and
|
Here is how you define a basic function that computes the sum of two
|
||||||
returns a single `int`:
|
integers:
|
||||||
|
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
function add(const a: int; const b: int): int is
|
function add (const a : int; const b : int) : int is
|
||||||
begin
|
block {
|
||||||
const result: int = a + b;
|
const sum : int = a + b
|
||||||
end with result;
|
} with sum
|
||||||
```
|
```
|
||||||
|
|
||||||
The function body consists of two parts:
|
The function body consists of two parts:
|
||||||
|
|
||||||
- `block {<code>}` - logic of the function
|
- `block { <instructions and declarations> }` - logic of the function
|
||||||
- `with <value>` - the return value of the function
|
- `with <value>` - the value returned by the function
|
||||||
|
|
||||||
#### Blockless functions
|
#### Blockless functions
|
||||||
|
|
||||||
Functions that can contain all of their logic into a single
|
Functions that can contain all of their logic into a single expression
|
||||||
instruction/expression, can be defined without the surrounding
|
can be defined without a block. Instead of a block, you put an
|
||||||
`block`. Instead, you can inline the necessary logic directly, like
|
expression, whose value is implicitly returned by the function, like
|
||||||
this:
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `add` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.ligo add '(1,2)'
|
||||||
|
# Outputs: 3
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
Functions in CameLIGO are defined using the `let` keyword, like value
|
Functions in CameLIGO are defined using the `let` keyword, like other
|
||||||
bindings. The difference is that after the value name a list of
|
values. The difference is that a succession of parameters is provided
|
||||||
function parameters is provided, along with a return type.
|
after the value name, followed by the return type. This follows OCaml
|
||||||
|
syntax. For example:
|
||||||
|
```cameligo group=c
|
||||||
|
let add (a : int) (b : int) : int = a + b
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `add` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.mligo add '(1,2)'
|
||||||
|
# Outputs: 3
|
||||||
|
```
|
||||||
|
|
||||||
CameLIGO is a little different from other syntaxes when it comes to
|
CameLIGO is a little different from other syntaxes when it comes to
|
||||||
function parameters. In OCaml, functions can only take one
|
function parameters. In OCaml, functions can only take one
|
||||||
parameter. To get functions with multiple arguments like we are used
|
parameter. To get functions with multiple arguments like we are used
|
||||||
to in traditional programming languages, a technique called
|
to in imperative programming languages, a technique called
|
||||||
[currying](https://en.wikipedia.org/wiki/Currying) is used. Currying
|
[currying](https://en.wikipedia.org/wiki/Currying) is used. Currying
|
||||||
essentially translates a function with multiple arguments into a
|
essentially translates a function with multiple arguments into a
|
||||||
series of single argument functions, each returning a new function
|
series of single argument functions, each returning a new function
|
||||||
accepting the next argument until every parameter is filled. This is
|
accepting the next argument until every parameter is filled. This is
|
||||||
useful because it means that CameLIGO can support
|
useful because it means that CameLIGO supports
|
||||||
[partial application](https://en.wikipedia.org/wiki/Partial_application).
|
[partial application](https://en.wikipedia.org/wiki/Partial_application).
|
||||||
|
|
||||||
Currying is however *not* the preferred way to pass function arguments
|
Currying is however *not* the preferred way to pass function arguments
|
||||||
in CameLIGO. While this approach is faithful to the original OCaml,
|
in CameLIGO. While this approach is faithful to the original OCaml,
|
||||||
it's costlier in Michelson than naive function execution accepting
|
it is costlier in Michelson than naive function execution accepting
|
||||||
multiple arguments. Instead for most functions with more than one
|
multiple arguments. Instead, for most functions with more than one
|
||||||
parameter we should place the arguments in a
|
parameter, we should gather the arguments in a
|
||||||
[tuple](language-basics/sets-lists-tuples.md) and pass the tuple in as
|
[tuple](language-basics/sets-lists-tuples.md) and pass the tuple in as
|
||||||
a single parameter.
|
a single parameter.
|
||||||
|
|
||||||
@ -87,56 +120,88 @@ Here is how you define a basic function that accepts two `ints` and
|
|||||||
returns an `int` as well:
|
returns an `int` as well:
|
||||||
|
|
||||||
```cameligo group=b
|
```cameligo group=b
|
||||||
let add (a,b: int * int) : int = a + b
|
let add (a, b : int * int) : int = a + b // Uncurried
|
||||||
|
let add_curry (a : int) (b : int) : int = add (a, b) // Curried
|
||||||
let add_curry (a: int) (b: int) : int = a + b
|
let increment (b : int) : int = add_curry 1 // Partial application
|
||||||
```
|
```
|
||||||
|
|
||||||
The function body is a series of expressions, which are evaluated to
|
You can run the `increment` function defined above using the LIGO
|
||||||
give the return value.
|
compiler like this:
|
||||||
|
```shell
|
||||||
|
ligo run-function gitlab-pages/docs/language-basics/src/functions/curry.mligo increment 5
|
||||||
|
# Outputs: 6
|
||||||
|
```
|
||||||
|
|
||||||
|
The function body is a single expression, whose value is returned.
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
Functions in ReasonLIGO are defined using the `let` keyword, like
|
Functions in ReasonLIGO are defined using the `let` keyword, like
|
||||||
value bindings. The difference is that after the value name a list of
|
other values. The difference is that a succession of parameters is
|
||||||
function parameters is provided, along with a return type.
|
provided after the value name, followed by the return type.
|
||||||
|
|
||||||
Here is how you define a basic function that accepts two `int`s and
|
|
||||||
returns an `int` as well:
|
|
||||||
|
|
||||||
|
Here is how you define a basic function that sums two integers:
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
let add = ((a,b): (int, int)) : int => a + b;
|
let add = ((a, b): (int, int)) : int => a + b;
|
||||||
```
|
```
|
||||||
|
|
||||||
The function body is a series of expressions, which are evaluated to
|
You can call the function `add` defined above using the LIGO compiler
|
||||||
give the return value.
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.religo add '(1,2)'
|
||||||
|
# Outputs: 3
|
||||||
|
```
|
||||||
|
|
||||||
|
The function body is a single expression, whose value is returned.
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Anonymous functions
|
## Anonymous functions (a.k.a. lambdas)
|
||||||
|
|
||||||
Functions without a name, also known as anonymous functions are useful
|
It is possible to define functions without assigning them a name. They
|
||||||
in cases when you want to pass the function as an argument or assign
|
are useful when you want to pass them as arguments, or assign them to
|
||||||
it to a key in a record or a map.
|
a key in a record or a map.
|
||||||
|
|
||||||
|
Here is how to define an anonymous function:
|
||||||
|
|
||||||
Here's how to define an anonymous function assigned to a variable
|
|
||||||
`increment`, with it is appropriate function type signature.
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const increment : int -> int = function (const i : int) : int is i + 1;
|
function increment (const b : int) : int is
|
||||||
// a = 2
|
(function (const a : int) : int is a + 1) (b)
|
||||||
const a: int = increment (1);
|
|
||||||
|
const a : int = increment (1); // a = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check the value of `a` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.ligo a
|
||||||
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let increment : int -> int = fun (i: int) -> i + 1
|
let increment (b : int) : int = (fun (a : int) -> a + 1) b
|
||||||
|
let a : int = increment 1 // a = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check the value of `a` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.mligo a
|
||||||
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let increment: (int => int) = (i: int) => i + 1;
|
let increment = (b : int) : int => ((a : int) : int => a + 1)(b);
|
||||||
|
let a : int = increment (1); // a = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check the value of `a` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.religo a
|
||||||
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,108 +3,229 @@ id: loops
|
|||||||
title: Loops
|
title: Loops
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## General Iteration
|
||||||
|
|
||||||
## While Loop
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
|
|
||||||
The PascaLIGO while loop should look familiar to users of imperative languages.
|
General iteration in PascaLIGO takes the shape of "while" loops, which
|
||||||
While loops are of the form `while <condition clause> <block>`, and evaluate
|
should be familiar to programmers of imperative languages. Those loops
|
||||||
their associated block until the condition evaluates to false.
|
are of the form `while <condition> <block>`. Their associated block is
|
||||||
|
repeatedly evaluated until the condition becomes true, or never
|
||||||
|
evaluated if the condition is false at the start. The loop never
|
||||||
|
terminates if the condition never becomes true. Because we are writing
|
||||||
|
smart contracts on Tezos, when the condition of a "while" loops fails
|
||||||
|
to become true, the execution will run out of gas and stop with a
|
||||||
|
failure anyway.
|
||||||
|
|
||||||
> ⚠️ The current PascaLIGO while loop has semantics that have diverged from other LIGO syntaxes. The goal of LIGO is that the various syntaxes express the same semantics, so this will be corrected in future versions. For details on how loops will likely work after refactoring, see the CameLIGO tab of this example.
|
Here is how to compute the greatest common divisors of two natural
|
||||||
|
number by means of Euclid's algorithm:
|
||||||
|
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
function while_sum (var n : nat) : nat is block {
|
function gcd (var x : nat; var y : nat) : nat is block {
|
||||||
var i : nat := 0n ;
|
if x < y then
|
||||||
var r : nat := 0n ;
|
block {
|
||||||
while i < n block {
|
const z : nat = x;
|
||||||
i := i + 1n;
|
x := y; y := z
|
||||||
r := r + i;
|
}
|
||||||
|
else skip;
|
||||||
|
var r : nat := 0n;
|
||||||
|
while y =/= 0n block {
|
||||||
|
r := x mod y;
|
||||||
|
x := y;
|
||||||
|
y := r
|
||||||
}
|
}
|
||||||
} with r
|
} with x
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `gcd` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/gcd.ligo gcd '(2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)'
|
||||||
|
# Outputs: +12
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
`Loop.fold_while` is a fold operation that takes an initial value of a certain type
|
CameLIGO is a functional language where user-defined values are
|
||||||
and then iterates on it until a condition is reached. The auxillary function
|
constant, therefore it makes no sense in CameLIGO to feature loops,
|
||||||
that does the fold returns either boolean true or boolean false to indicate
|
which we understand as syntactic constructs where the state of a
|
||||||
whether the fold should continue or not. The initial value must match the input
|
stopping condition is mutated, as with "while" loops in PascaLIGO.
|
||||||
parameter of the auxillary function, and the auxillary should return type `(bool * input)`.
|
|
||||||
|
|
||||||
`continue` and `stop` are provided as syntactic sugar for the return values.
|
Instead, CameLIGO features a *fold operation* as a predefined function
|
||||||
|
named `Loop.fold_while`. It takes an initial value of a certain type,
|
||||||
|
called an *accumulator*, and repeatedly calls a given function, called
|
||||||
|
*iterated function*, that takes that accumulator and returns the next
|
||||||
|
value of the accumulator, until a condition is met and the fold stops
|
||||||
|
with the final value of the accumulator. The iterated function needs
|
||||||
|
to have a special type: if the type of the accumulator is `t`, then it
|
||||||
|
must have the type `bool * t` (not simply `t`). It is the boolean
|
||||||
|
value that denotes whether the stopping condition has been reached.
|
||||||
|
|
||||||
```cameligo
|
```cameligo group=b
|
||||||
let aux (i: int) : bool * int =
|
let iter (x,y : nat * nat) : bool * (nat * nat) =
|
||||||
if i < 100 then continue (i + 1) else stop i
|
if y = 0n then false, (x,y) else true, (y, x mod y)
|
||||||
|
|
||||||
let counter_simple (n: int) : int =
|
let gcd (x,y : nat * nat) : nat =
|
||||||
Loop.fold_while aux n
|
let x,y = if x < y then y,x else x,y in
|
||||||
|
let x,y = Loop.fold_while iter (x,y)
|
||||||
|
in x
|
||||||
|
```
|
||||||
|
|
||||||
|
To ease the writing and reading of the iterated functions (here,
|
||||||
|
`iter`), two predefined functions are provided: `continue` and `stop`:
|
||||||
|
|
||||||
|
```cameligo group=c
|
||||||
|
let iter (x,y : nat * nat) : bool * (nat * nat) =
|
||||||
|
if y = 0n then stop (x,y) else continue (y, x mod y)
|
||||||
|
|
||||||
|
let gcd (x,y : nat * nat) : nat =
|
||||||
|
let x,y = if x < y then y,x else x,y in
|
||||||
|
let x,y = Loop.fold_while iter (x,y)
|
||||||
|
in x
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `gcd` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/gcd.mligo gcd (2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)'
|
||||||
|
# Outputs: +12
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
`Loop.fold_while` is a fold operation that takes an initial value of a certain type
|
ReasonLIGO is a functional language where user-defined values are
|
||||||
and then iterates on it until a condition is reached. The auxillary function
|
constant, therefore it makes no sense in ReasonLIGO to feature loops,
|
||||||
that does the fold returns either boolean true or boolean false to indicate
|
which we understand as syntactic constructs where the state of a
|
||||||
whether the fold should continue or not. The initial value must match the input
|
stopping condition is mutated, as with "while" loops in PascaLIGO.
|
||||||
parameter of the auxillary function, and the auxillary should return type `(bool, input)`.
|
|
||||||
|
|
||||||
`continue` and `stop` are provided as syntactic sugar for the return values.
|
Instead, ReasonLIGO features a *fold operation* as a predefined
|
||||||
|
function named `Loop.fold_while`. It takes an initial value of a
|
||||||
|
certain type, called an *accumulator*, and repeatedly calls a given
|
||||||
|
function, called *iterated function*, that takes that accumulator and
|
||||||
|
returns the next value of the accumulator, until a condition is met
|
||||||
|
and the fold stops with the final value of the accumulator. The
|
||||||
|
iterated function needs to have a special type: if the type of the
|
||||||
|
accumulator is `t`, then it must have the type `bool * t` (not simply
|
||||||
|
`t`). It is the boolean value that denotes whether the stopping
|
||||||
|
condition has been reached.
|
||||||
|
|
||||||
```reasonligo
|
```reasonligo group=d
|
||||||
let aux = (i: int): (bool, int) =>
|
let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) =>
|
||||||
if (i < 100) {
|
if (y == 0n) { (false, (x,y)); } else { (true, (y, x mod y)); };
|
||||||
continue(i + 1);
|
|
||||||
} else {
|
|
||||||
stop(i);
|
|
||||||
};
|
|
||||||
|
|
||||||
let counter_simple = (n: int): int => Loop.fold_while(aux, n);
|
let gcd = ((x,y) : (nat, nat)) : nat =>
|
||||||
|
let (x,y) = if (x < y) { (y,x); } else { (x,y); };
|
||||||
|
let (x,y) = Loop.fold_while (iter, (x,y));
|
||||||
|
x;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To ease the writing and reading of the iterated functions (here,
|
||||||
|
`iter`), two predefined functions are provided: `continue` and `stop`:
|
||||||
|
|
||||||
|
```reasonligo group=e
|
||||||
|
let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) =>
|
||||||
|
if (y == 0n) { stop ((x,y)); } else { continue ((y, x mod y)); };
|
||||||
|
|
||||||
|
let gcd = ((x,y) : (nat, nat)) : nat =>
|
||||||
|
let (x,y) = if (x < y) { (y,x); } else { (x,y); };
|
||||||
|
let (x,y) = Loop.fold_while (iter, (x,y));
|
||||||
|
x;
|
||||||
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## For Loop
|
## For Loops
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
|
|
||||||
To iterate over a range of integers you use a loop of the form `for <variable assignment> to <integer> <block>`.
|
To iterate over a range of integers you use a loop of the form `for
|
||||||
|
<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
|
```pascaligo group=f
|
||||||
function for_sum (var n : nat) : int is block {
|
function sum (var n : nat) : int is block {
|
||||||
var acc : int := 0 ;
|
var acc : int := 0;
|
||||||
for i := 1 to int(n)
|
for i := 1 to int (n) block {
|
||||||
begin
|
acc := acc + i
|
||||||
acc := acc + i;
|
}
|
||||||
end
|
|
||||||
} with acc
|
} with acc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can call the function `sum` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/sum.ligo sum 7n
|
||||||
|
# Outputs: 28
|
||||||
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
|
|
||||||
PascaLIGO for loops can also iterate through the contents of a collection. This is
|
PascaLIGO "for" loops can also iterate through the contents of a
|
||||||
done with a loop of the form `for <element var> in <collection type> <collection var> <block>`.
|
collection, that is, a list, a set or a map. This is done with a loop
|
||||||
|
of the form `for <element var> in <collection type> <collection var>
|
||||||
|
<block>`, where `<collection type` is any of the following keywords:
|
||||||
|
`list`, `set` or `map`.
|
||||||
|
|
||||||
```pascaligo
|
Here is an example where the integers in a list are summed up.
|
||||||
function for_collection_list (var nee : unit) : (int * string) is block {
|
|
||||||
var acc : int := 0;
|
```pascaligo group=g
|
||||||
var st : string := "to";
|
function sum_list (var l : list (int)) : int is block {
|
||||||
var mylist : list(int) := list 1; 1; 1 end;
|
var total : int := 0;
|
||||||
for x in list mylist
|
for i in list l block {
|
||||||
begin
|
total := total + i
|
||||||
acc := acc + x;
|
}
|
||||||
st := st ^ "to";
|
} with total
|
||||||
end
|
|
||||||
} with (acc, st)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can call the function `sum_list` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_list
|
||||||
|
'list [1;2;3]'
|
||||||
|
# Outputs: 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is an example where the integers in a set are summed up.
|
||||||
|
|
||||||
|
```pascaligo=g
|
||||||
|
function sum_set (var s : set (int)) : int is block {
|
||||||
|
var total : int := 0;
|
||||||
|
for i in set s block {
|
||||||
|
total := total + i
|
||||||
|
}
|
||||||
|
} with total
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `sum_set` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_set
|
||||||
|
'set [1;2;3]'
|
||||||
|
# Outputs: 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Loops over maps are actually loops over the bindings of the map, that
|
||||||
|
is, a pair key-value noted `key -> value` (or any other
|
||||||
|
variables). Give a map from strings to integers, here is how to sum
|
||||||
|
all the integers and concatenate all the strings.
|
||||||
|
|
||||||
|
You can call the function `sum_map` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_map
|
||||||
|
'map ["1"->1; "2"->2; "3"->3]'
|
||||||
|
# Outputs: ( "123", 6 )
|
||||||
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,120 +7,156 @@ LIGO offers three built-in numerical types: `int`, `nat` and `tez`.
|
|||||||
|
|
||||||
## Addition
|
## Addition
|
||||||
|
|
||||||
Addition in ligo is acomplished by using the `+` operator. Some type constraints apply; for example you can't add `tez + nat`.
|
Addition in LIGO is accomplished by means of the `+` infix
|
||||||
|
operator. Some type constraints apply, for example you ca not add a
|
||||||
|
value of type `tez` to a value of type `nat`.
|
||||||
|
|
||||||
In the following example you can find a series of arithmetic operations, including various numerical types. However, some bits of the example won't compile because adding an `int` to a `nat` produces an `int`, not a `nat`. Similiar rules apply for `tez`:
|
In the following example you can find a series of arithmetic
|
||||||
|
operations, including various numerical types. However, some bits
|
||||||
|
remain in comments because they would otherwise not compile because,
|
||||||
|
for example, adding a value of type `int` to a value of type `tez` is
|
||||||
|
invalid. Note that adding an integer to a natural number produces an
|
||||||
|
integer.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
|
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
// int + int produces int
|
// int + int yields int
|
||||||
const a: int = 5 + 10;
|
const a : int = 5 + 10
|
||||||
// nat + int produces int
|
|
||||||
const b: int = 5n + 10;
|
// nat + int yields int
|
||||||
// tez + tez produces tez
|
const b : int = 5n + 10
|
||||||
const c: tez = 5mutez + 10mutez;
|
|
||||||
// you can't add tez + int or tez + nat, this won't compile
|
// tez + tez yields tez
|
||||||
// const d: tez = 5mutez + 10n;
|
const c : tez = 5mutez + 10mutez
|
||||||
// two nats produce a nat
|
|
||||||
const e: nat = 5n + 10n;
|
//tez + int or tez + nat is invalid
|
||||||
// nat + int produces an int, this won't compile
|
// const d : tez = 5mutez + 10n
|
||||||
// const f: nat = 5n + 10;
|
|
||||||
const g: int = 1_000_000;
|
// two nats yield a nat
|
||||||
|
const e : nat = 5n + 10n
|
||||||
|
|
||||||
|
// nat + int yields an int: invalid
|
||||||
|
// const f : nat = 5n + 10;
|
||||||
|
|
||||||
|
const g : int = 1_000_000
|
||||||
```
|
```
|
||||||
|
|
||||||
> Pro tip: you can use underscores for readability when defining large numbers
|
> Pro tip: you can use underscores for readability when defining large
|
||||||
|
> numbers:
|
||||||
>
|
>
|
||||||
>```pascaligo
|
>```pascaligo
|
||||||
>const g: int = 1_000_000;
|
> const sum : tez = 100_000mutez
|
||||||
>```
|
>```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
// int + int produces int
|
// int + int yields int
|
||||||
let a: int = 5 + 10
|
let a : int = 5 + 10
|
||||||
// nat + int produces int
|
|
||||||
let b: int = 5n + 10
|
// nat + int yields int
|
||||||
// tez + tez produces tez
|
let b : int = 5n + 10
|
||||||
let c: tez = 5mutez + 10mutez
|
|
||||||
// you can't add tez + int or tez + nat, this won't compile
|
// tez + tez yields tez
|
||||||
// const d: tez = 5mutez + 10n
|
let c : tez = 5mutez + 10mutez
|
||||||
// two nats produce a nat
|
|
||||||
let e: nat = 5n + 10n
|
// tez + int or tez + nat is invalid
|
||||||
// nat + int produces an int, this won't compile
|
// const d : tez = 5mutez + 10n
|
||||||
// const f: nat = 5n + 10
|
|
||||||
let g: int = 1_000_000
|
// two nats yield a nat
|
||||||
|
let e : nat = 5n + 10n
|
||||||
|
|
||||||
|
// nat + int yields an int: invalid
|
||||||
|
// const f : nat = 5n + 10
|
||||||
|
|
||||||
|
let g : int = 1_000_000
|
||||||
```
|
```
|
||||||
|
|
||||||
> Pro tip: you can use underscores for readability when defining large numbers
|
> Pro tip: you can use underscores for readability when defining large
|
||||||
|
> numbers:
|
||||||
>
|
>
|
||||||
>```cameligo
|
>```cameligo
|
||||||
>let g: int = 1_000_000;
|
>let sum : tez = 100_000mutez
|
||||||
>```
|
>```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
(* int + int produces int *)
|
// int + int yields int
|
||||||
let a: int = 5 + 10;
|
let a : int = 5 + 10;
|
||||||
(* nat + int produces int *)
|
|
||||||
let b: int = 5n + 10;
|
// nat + int yields int
|
||||||
(* tez + tez produces tez *)
|
let b : int = 5n + 10;
|
||||||
let c: tez = 5mutez + 10mutez;
|
|
||||||
(* you can't add tez + int or tez + nat, this won't compile:
|
// tez + tez yields tez
|
||||||
let d: tez = 5mutez + 10n; *)
|
let c : tez = 5mutez + 10mutez;
|
||||||
(* two nats produce a nat *)
|
|
||||||
let e: nat = 5n + 10n;
|
// tez + int or tez + nat is invalid:
|
||||||
(* nat + int produces an int, this won't compile:
|
// let d : tez = 5mutez + 10n;
|
||||||
let f: nat = 5n + 10; *)
|
|
||||||
let g: int = 1_000_000;
|
// two nats yield a nat
|
||||||
|
let e : nat = 5n + 10n;
|
||||||
|
|
||||||
|
// nat + int yields an int: invalid
|
||||||
|
//let f : nat = 5n + 10;
|
||||||
|
|
||||||
|
let g : int = 1_000_000;
|
||||||
```
|
```
|
||||||
|
|
||||||
> Pro tip: you can use underscores for readability when defining large numbers
|
> Pro tip: you can use underscores for readability when defining large
|
||||||
>
|
> numbers:
|
||||||
>```reasonligo
|
>```reasonligo
|
||||||
>let g: int = 1_000_000;
|
>let sum : tex = 100_000mutez;
|
||||||
>```
|
>```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Subtraction
|
## Subtraction
|
||||||
|
|
||||||
The simpliest substraction looks like this:
|
Subtraction looks like 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
|
||||||
// substraction of two nats, yields an int
|
|
||||||
const b: int = 5n - 2n;
|
// Subtraction of two nats yields an int
|
||||||
// won't compile, result is an int, not a nat
|
const b : int = 5n - 2n
|
||||||
// const c: nat = 5n - 2n;
|
|
||||||
const d: tez = 5mutez - 1mutez;
|
// Therefore the following is invalid
|
||||||
|
// const c : nat = 5n - 2n
|
||||||
|
|
||||||
|
const d : tez = 5mutez - 1mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=b
|
```cameligo group=b
|
||||||
let a: int = 5 - 10
|
let a : int = 5 - 10
|
||||||
// substraction of two nats, yields an int
|
|
||||||
let b: int = 5n - 2n
|
// Subtraction of two nats yields an int
|
||||||
// won't compile, result is an int, not a nat
|
let b : int = 5n - 2n
|
||||||
// const c: nat = 5n - 2n
|
|
||||||
let d: tez = 5mutez - 1mutez
|
// Therefore the following is invalid
|
||||||
|
// const c : nat = 5n - 2n
|
||||||
|
|
||||||
|
let d : tez = 5mutez - 1mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
let a: int = 5 - 10;
|
let a : int = 5 - 10;
|
||||||
(* substraction of two nats, yields an int *)
|
|
||||||
let b: int = 5n - 2n;
|
// Subtraction of two nats yields an int
|
||||||
(* won't compile, result is an int, not a nat *)
|
let b : int = 5n - 2n;
|
||||||
(* let c: nat = 5n - 2n; *)
|
|
||||||
let d: tez = 5mutez - 1mutez;
|
// Therefore the following is invalid
|
||||||
|
// let c : nat = 5n - 2n;
|
||||||
|
|
||||||
|
let d : tez = 5mutez - 1mutez;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
@ -134,88 +170,106 @@ You can multiply values of the same type, such as:
|
|||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
|
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const a: int = 5 * 5;
|
const a : int = 5 * 5
|
||||||
const b: nat = 5n * 5n;
|
const b : nat = 5n * 5n
|
||||||
// you can also multiply `nat` and `tez`
|
// You can also multiply `nat` and `tez` in any order
|
||||||
const c: tez = 5n * 5mutez;
|
const c : tez = 5n * 5mutez;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let a: int = 5 * 5
|
let a : int = 5 * 5
|
||||||
let b: nat = 5n * 5n
|
let b : nat = 5n * 5n
|
||||||
// you can also multiply `nat` and `tez`
|
// You can also multiply `nat` and `tez` in any order
|
||||||
let c: tez = 5n * 5mutez
|
let c : tez = 5n * 5mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let a: int = 5 * 5;
|
let a : int = 5 * 5;
|
||||||
let b: nat = 5n * 5n;
|
let b : nat = 5n * 5n;
|
||||||
(* you can also multiply `nat` and `tez` *)
|
// You can also multiply `nat` and `tez` in any order
|
||||||
let c: tez = 5n * 5mutez;
|
let c : tez = 5n * 5mutez;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Division
|
## Division
|
||||||
|
|
||||||
In LIGO you can divide `int`, `nat`, and `tez`. Here's how:
|
In LIGO you can divide `int`, `nat`, and `tez`. Here is how:
|
||||||
|
|
||||||
> ⚠️ Division of two `tez` values results into a `nat`
|
> ⚠️ 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
|
||||||
const c: nat = 10mutez / 3mutez;
|
const c : nat = 10mutez / 3mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=d
|
```cameligo group=d
|
||||||
let a: int = 10 / 3
|
let a : int = 10 / 3
|
||||||
let b: nat = 10n / 3n
|
let b : nat = 10n / 3n
|
||||||
let c: nat = 10mutez / 3mutez
|
let c : nat = 10mutez / 3mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=d
|
```reasonligo group=d
|
||||||
let a: int = 10 / 3;
|
let a : int = 10 / 3;
|
||||||
let b: nat = 10n / 3n;
|
let b : nat = 10n / 3n;
|
||||||
let c: nat = 10mutez / 3mutez;
|
let c : nat = 10mutez / 3mutez;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## From `int` to `nat` and back
|
## From `int` to `nat` and back
|
||||||
|
|
||||||
You can *cast* an `int` to a `nat` and vice versa, here's how:
|
You can *cast* an `int` to a `nat` and vice versa. Here is how:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=e
|
```pascaligo group=e
|
||||||
const a: int = int(1n);
|
const a : int = int (1n)
|
||||||
const b: nat = abs(1);
|
const b : nat = abs (1)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=e
|
||||||
|
let a : int = int (1n)
|
||||||
|
let b : nat = abs (1)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=e
|
||||||
|
let a : int = int (1n);
|
||||||
|
let b : nat = abs (1);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Check if a value is a `nat`
|
## Check if a value is a `nat`
|
||||||
|
|
||||||
You can check if a value is a `nat`, by using a syntax specific built-in function, which accepts an `int` and returns an `option(nat)`, more specifically `Some(nat)` if the provided integer is a natural number, and `None` otherwise:
|
You can check if a value is a `nat` by using a syntax specific
|
||||||
|
built-in function, which accepts an `int` and returns an optional
|
||||||
|
`nat`: if `Some(nat)` then the provided integer was indeed a natural
|
||||||
|
number, and not otherwise.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=e
|
||||||
const its_a_nat: option(nat) = is_nat(1)
|
const is_a_nat : option (nat) = is_nat (1)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=e
|
||||||
|
let is_a_nat : nat option = Michelson.is_nat (1)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=e
|
```reasonligo group=e
|
||||||
let a: int = int(1n);
|
let is_a_nat : option (nat) = Michelson.is_nat (1);
|
||||||
let b: nat = abs(1);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -1,329 +1,79 @@
|
|||||||
---
|
---
|
||||||
id: sets-lists-tuples
|
id: sets-lists-tuples
|
||||||
title: Sets, Lists, Tuples
|
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 `sets`, `lists` and `tuples`.
|
exposes `tuples`, `lists` and `sets`.
|
||||||
|
|
||||||
> ⚠️ Make sure to pick the appropriate data type for your use case; it carries not only semantic but also gas related costs.
|
|
||||||
|
|
||||||
## Sets
|
|
||||||
|
|
||||||
Sets are similar to lists. The main difference is that elements of a
|
|
||||||
`set` must be *unique*.
|
|
||||||
|
|
||||||
### Defining a set
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
type int_set is set (int);
|
|
||||||
const my_set : int_set = set 1; 2; 3 end
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=a
|
|
||||||
type int_set = int set
|
|
||||||
let my_set : int_set =
|
|
||||||
Set.add 3 (Set.add 2 (Set.add 1 (Set.empty: int set)))
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=a
|
|
||||||
type int_set = set (int);
|
|
||||||
let my_set : int_set =
|
|
||||||
Set.add (3, Set.add (2, Set.add (1, Set.empty: set (int))));
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
### Empty sets
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
const my_set: int_set = set end
|
|
||||||
const my_set_2: int_set = set_empty
|
|
||||||
```
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=a
|
|
||||||
let my_set: int_set = (Set.empty: int set)
|
|
||||||
```
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=a
|
|
||||||
let my_set: int_set = (Set.empty: set (int));
|
|
||||||
```
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
### Checking if set contains an element
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
const contains_three : bool = my_set contains 3
|
|
||||||
// or alternatively
|
|
||||||
const contains_three_fn: bool = set_mem (3, my_set);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=a
|
|
||||||
let contains_three: bool = Set.mem 3 my_set
|
|
||||||
```
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=a
|
|
||||||
let contains_three: bool = Set.mem(3, my_set);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
|
||||||
### Obtaining the size of a set
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
const set_size: nat = size (my_set)
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=a
|
|
||||||
let set_size: nat = Set.size my_set
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=a
|
|
||||||
let set_size: nat = Set.size (my_set);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
|
||||||
### Modifying a set
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
const larger_set: int_set = set_add(4, my_set);
|
|
||||||
const smaller_set: int_set = set_remove(3, my_set);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
|
|
||||||
```cameligo group=a
|
|
||||||
let larger_set: int_set = Set.add 4 my_set
|
|
||||||
let smaller_set: int_set = Set.remove 3 my_set
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
|
|
||||||
```reasonligo group=a
|
|
||||||
let larger_set: int_set = Set.add(4, my_set);
|
|
||||||
let smaller_set: int_set = Set.remove(3, my_set);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
|
||||||
### Folding a set
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=a
|
|
||||||
function sum(const result: int; const i: int): int is result + i;
|
|
||||||
// Outputs 6
|
|
||||||
const sum_of_a_set: int = set_fold(sum, my_set, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=a
|
|
||||||
let sum (result, i: int * int) : int = result + i
|
|
||||||
let sum_of_a_set: int = Set.fold sum my_set 0
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=a
|
|
||||||
let sum = (result_i: (int, int)): int => result_i[0] + result_i[1];
|
|
||||||
let sum_of_a_set: int = Set.fold(sum, my_set, 0);
|
|
||||||
```
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
## Lists
|
|
||||||
|
|
||||||
Lists are similar to sets, but their elements don't need to be unique and they don't offer the same range of built-in functions.
|
|
||||||
|
|
||||||
> 💡 Lists are useful when returning operations from a smart contract's entrypoint.
|
|
||||||
|
|
||||||
### Defining a list
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=b
|
|
||||||
type int_list is list(int);
|
|
||||||
const my_list: int_list = list
|
|
||||||
1;
|
|
||||||
2;
|
|
||||||
3;
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=b
|
|
||||||
type int_list = int list
|
|
||||||
let my_list: int_list = [1; 2; 3]
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=b
|
|
||||||
type int_list = list(int);
|
|
||||||
let my_list: int_list = [1, 2, 3];
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
|
||||||
### Appending an element to a list
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=b
|
|
||||||
const larger_list: int_list = cons(4, my_list);
|
|
||||||
const even_larger_list: int_list = 5 # larger_list;
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo group=b
|
|
||||||
let larger_list: int_list = 4 :: my_list
|
|
||||||
(* CameLIGO doesn't have a List.cons *)
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo group=b
|
|
||||||
let larger_list: int_list = [4, ...my_list];
|
|
||||||
(* ReasonLIGO doesn't have a List.cons *)
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
> 💡 Lists can be iterated, folded or mapped to different values. You can find additional examples [here](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts) and other built-in operators [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L59)
|
|
||||||
|
|
||||||
### Mapping of a list
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=b
|
|
||||||
function increment(const i: int): int is block { skip } with i + 1;
|
|
||||||
// Creates a new list with elements incremented by 1
|
|
||||||
const incremented_list: int_list = list_map(increment, even_larger_list);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
|
|
||||||
```cameligo group=b
|
|
||||||
let increment (i: int) : int = i + 1
|
|
||||||
(* Creates a new list with elements incremented by 1 *)
|
|
||||||
let incremented_list: int_list = List.map increment larger_list
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
|
|
||||||
```reasonligo group=b
|
|
||||||
let increment = (i: int): int => i + 1;
|
|
||||||
(* Creates a new list with elements incremented by 1 *)
|
|
||||||
let incremented_list: int_list = List.map(increment, larger_list);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
|
||||||
### Folding of a list:
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo group=b
|
|
||||||
function sum(const result: int; const i: int): int is block { skip } with result + i;
|
|
||||||
// Outputs 6
|
|
||||||
const sum_of_a_list: int = list_fold(sum, my_list, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
|
|
||||||
```cameligo group=b
|
|
||||||
let sum (result, i: int * int) : int = result + i
|
|
||||||
// Outputs 6
|
|
||||||
let sum_of_a_list: int = List.fold sum my_list 0
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
|
|
||||||
```reasonligo group=b
|
|
||||||
let sum = ((result, i): (int, int)): int => result + i;
|
|
||||||
(* Outputs 6 *)
|
|
||||||
let sum_of_a_list: int = List.fold(sum, my_list, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
||||||
|
|
||||||
|
> ⚠️ Make sure to pick the appropriate data type for your use case, and
|
||||||
|
> bear in mind the related gas costs.
|
||||||
|
|
||||||
## Tuples
|
## Tuples
|
||||||
|
|
||||||
Tuples are used to store related data that has a **specific order** and **defined
|
Tuples gather a given number of values in a specific order and those
|
||||||
length** without the need for named fields or a dedicated type identity. Probably
|
values, called *components*, can be retrieved by their index
|
||||||
the most common tuple is a pair of type `(a, b)`. For example, if we were storing
|
(position). Probably the most common tuple is the *pair*. For
|
||||||
coordinates on a two dimensional grid we might use a pair tuple of type `int * int`
|
example, if we were storing coordinates on a two dimensional grid we
|
||||||
to store the coordinates x and y. There is a **specific order** because x and y must
|
might use a pair of type `int * int` to store the coordinates `x` and
|
||||||
always stay in the same location within the tuple for the data to make sense. There is
|
`y` as the pair value `(x,y)`. There is a *specific order*, so `(y,x)`
|
||||||
also a **defined length** because the tuple pair can only ever have two elements,
|
is not equal to `(x,y)`. The number of components is part of the type
|
||||||
if we added a third dimension `z` its type would be incompatible with that of the
|
of a tuple, so, for example, we cannot add an extra component to a
|
||||||
pair tuple.
|
pair and obtain a triple of the same type: `(x,y)` has always a
|
||||||
|
different type from `(x,y,z)`, whereas `(y,x)` may have the same type.
|
||||||
|
|
||||||
Like records, tuples can have members of arbitrary types in the same structure.
|
Like records, tuple components can be of arbitrary types.
|
||||||
|
|
||||||
### Defining a tuple
|
### Defining a tuple
|
||||||
|
|
||||||
Unlike [a record](language-basics/maps-records.md), tuple types do not have to be
|
Unlike [a record](language-basics/maps-records.md), tuple types do not
|
||||||
defined before they can be used. However below we will give them names for the
|
have to be defined before they can be used. However below we will give
|
||||||
sake of illustration.
|
them names by *type aliasing*.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
type full_name is string * string;
|
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=c
|
||||||
type full_name = string * string
|
type full_name = string * string // Alias
|
||||||
|
|
||||||
(* The parenthesis here are optional *)
|
(* The parenthesis here are optional *)
|
||||||
let full_name: full_name = ("Alice", "Johnson")
|
let full_name : full_name = ("Alice", "Johnson")
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
type full_name = (string, string);
|
type full_name = (string, string); // Alias
|
||||||
|
|
||||||
(* The parenthesis here are optional *)
|
(* 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 an Element in a Tuple
|
||||||
|
|
||||||
The traditional way to access the elements of a tuple in OCaml is through
|
Accessing the components of a tuple in OCaml is achieved by
|
||||||
[a pattern match](language-basics/unit-option-pattern-matching.md). LIGO **does
|
[pattern matching](language-basics/unit-option-pattern-matching.md). LIGO
|
||||||
not** currently support tuple patterns in its syntaxes.
|
currently supports tuple patterns only in the parameters of functions,
|
||||||
|
not in pattern matching. In LIGO, however, we can access components by
|
||||||
However, it is possible to access LIGO tuples by their position.
|
their position in their tuple, which cannot be done in OCaml.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
|
|
||||||
Tuple elements are one-indexed and accessed like so:
|
Tuple components are one-indexed like so:
|
||||||
|
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const first_name: string = full_name.1;
|
const first_name : string = full_name.1;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--Cameligo-->
|
<!--Cameligo-->
|
||||||
@ -331,12 +81,426 @@ const first_name: string = full_name.1;
|
|||||||
Tuple elements are zero-indexed and accessed like so:
|
Tuple elements are zero-indexed and accessed like so:
|
||||||
|
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let first_name: string = full_name.0
|
let first_name : string = full_name.0
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
Tuple components are one-indexed like so:
|
||||||
|
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let first_name: string = full_name[1];
|
let first_name : string = full_name[1];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lists
|
||||||
|
|
||||||
|
Lists are linear collections of elements of the same type. Linear
|
||||||
|
means that, in order to reach an element in a list, we must visit all
|
||||||
|
the elements before (sequential access). Elements can be repeated, as
|
||||||
|
only their order in the collection matters. The first element is
|
||||||
|
called the *head*, and the sub-list after the head is called the
|
||||||
|
*tail*. For those familiar with algorithmic data structure, you can
|
||||||
|
think of a list a *stack*, where the top is written on the left.
|
||||||
|
|
||||||
|
> 💡 Lists are useful when returning operations from a smart
|
||||||
|
> contract's entrypoint.
|
||||||
|
|
||||||
|
### Defining a List
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=b
|
||||||
|
const my_list : list (int) = list [1; 2; 2] // The head is 1
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=b
|
||||||
|
let my_list : int list = [1; 2; 2] // The head is 1
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=b
|
||||||
|
let my_list : list (int) = [1, 2, 2]; // The head is 1
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
### Adding to a List
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
Lists can be augmented by adding an element before the head (or, in
|
||||||
|
terms of stack, by *pushing an element on top*). This operation is
|
||||||
|
usually called *consing* in functional languages.
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
In PascaLIGO, the *cons operator* is infix and noted `#`. It is not
|
||||||
|
symmetric: on the left lies the element to cons, and, on the right, a
|
||||||
|
list on which to cons. (The symbol is helpfully asymmetric to remind
|
||||||
|
you of that.)
|
||||||
|
|
||||||
|
```pascaligo group=b
|
||||||
|
const larger_list : list (int) = 5 # my_list
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, the *cons operator* is infix and noted `::`. It is not
|
||||||
|
symmetric: on the left lies the element to cons, and, on the right, a
|
||||||
|
list on which to cons.
|
||||||
|
|
||||||
|
```cameligo group=b
|
||||||
|
let larger_list : int list = 5 :: my_list
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
In ReasonLIGO, the *cons operator* is infix and noted `, ...`. It is
|
||||||
|
not symmetric: on the left lies the element to cons, and, on the
|
||||||
|
right, a list on which to cons.
|
||||||
|
|
||||||
|
```reasonligo group=b
|
||||||
|
let larger_list : list (int) = [5, ...my_list];
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
> 💡 Lists can be iterated, folded or mapped to different values. You
|
||||||
|
> can find additional examples
|
||||||
|
> [here](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts)
|
||||||
|
> and other built-in operators
|
||||||
|
> [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L59)
|
||||||
|
|
||||||
|
### Mapping of a List
|
||||||
|
|
||||||
|
We may want to apply a function to all the elements of a list and
|
||||||
|
obtain the resulting list, in the same order. For example, we may want
|
||||||
|
to create a list that contains all the elements of another list
|
||||||
|
incremented by one. This is a special case of *fold operation* called
|
||||||
|
a *map operation*. Map operations (not to be confused by the
|
||||||
|
[map data structure](language-basics/maps-records.md)), are predefined
|
||||||
|
functions in LIGO. They take as a parameter the function to apply to
|
||||||
|
all the elements. Of course, that function must return a value of the
|
||||||
|
same type as the element.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
In PascaLIGO, the map function is called `list_map`.
|
||||||
|
|
||||||
|
```pascaligo group=b
|
||||||
|
function increment (const i : int): int is i + 1
|
||||||
|
|
||||||
|
// Creates a new list with all elements incremented by 1
|
||||||
|
const plus_one : list (int) = list_map (increment, larger_list)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, the map function is called `List.map`.
|
||||||
|
|
||||||
|
```cameligo group=b
|
||||||
|
let increment (i : int) : int = i + 1
|
||||||
|
|
||||||
|
// Creates a new list with all elements incremented by 1
|
||||||
|
let plus_one : int list = List.map increment larger_list
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, the map function is called `List.map`.
|
||||||
|
|
||||||
|
```reasonligo group=b
|
||||||
|
let increment = (i : int) : int => i + 1;
|
||||||
|
|
||||||
|
// Creates a new list with all elements incremented by 1
|
||||||
|
let plus_one : list (int) = List.map (increment, larger_list);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
### Folding of over a List
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=b
|
||||||
|
function sum (const acc : int; const i : int): int is acc + i
|
||||||
|
|
||||||
|
const sum_of_elements : int = list_fold (sum, my_list, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo group=b
|
||||||
|
let sum (acc, i: int * int) : int = acc + i
|
||||||
|
let sum_of_elements : int = List.fold sum my_list 0
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo group=b
|
||||||
|
let sum = ((result, i): (int, int)): int => result + i;
|
||||||
|
let sum_of_elements : int = List.fold (sum, my_list, 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
## Sets
|
||||||
|
|
||||||
|
Sets are unordered collections of values of the same type, like lists
|
||||||
|
are ordered collections. Like the mathematical sets and lists, sets
|
||||||
|
can be empty and, if not, elements of sets in LIGO are *unique*,
|
||||||
|
whereas they can be repeated in a list.
|
||||||
|
|
||||||
|
### Empty Sets
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=a
|
||||||
|
const my_set : set (int) = set []
|
||||||
|
```
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=a
|
||||||
|
let my_set : int set = (Set.empty : int set)
|
||||||
|
```
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=a
|
||||||
|
let my_set : set (int) = (Set.empty : set (int));
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
### Non-empty Sets
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
In PascaLIGO, the notation for sets is similar to that for lists,
|
||||||
|
except the keyword `set` is used before:
|
||||||
|
|
||||||
|
```pascaligo group=a
|
||||||
|
const my_set : set (int) = set [3; 2; 2; 1]
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check that `2` is not repeated in `my_set` by using the LIGO
|
||||||
|
compiler like this (the output will sort the elements of the set, but
|
||||||
|
that order is not significant for the compiler):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ligo evaluate-value
|
||||||
|
gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo my_set
|
||||||
|
# Outputs: { 3 ; 2 ; 1 }
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, there is no predefined syntactic construct for sets: you
|
||||||
|
must build your set by adding to the empty set. (This is the way in
|
||||||
|
OCaml.)
|
||||||
|
|
||||||
|
```cameligo group=a
|
||||||
|
let my_set : int set =
|
||||||
|
Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set))))
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check that `2` is not repeated in `my_set` by using the LIGO
|
||||||
|
compiler like this (the output will sort the elements of the set, but
|
||||||
|
that order is not significant for the compiler):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ligo evaluate-value
|
||||||
|
gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo my_set
|
||||||
|
# Outputs: { 3 ; 2 ; 1 }
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
In ReasonLIGO, there is no predefined syntactic construct for sets:
|
||||||
|
you must build your set by adding to the empty set. (This is the way
|
||||||
|
in OCaml.)
|
||||||
|
|
||||||
|
```reasonligo group=a
|
||||||
|
let my_set : set (int) =
|
||||||
|
Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int)))));
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check that `2` is not repeated in `my_set` by using the LIGO
|
||||||
|
compiler like this (the output will sort the elements of the set, but
|
||||||
|
that order is not significant for the compiler):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ligo evaluate-value
|
||||||
|
gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo my_set
|
||||||
|
# Outputs: { 3 ; 2 ; 1 }
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
### Set Membership
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
PascaLIGO features a special keyword `constains` that operates like an
|
||||||
|
infix operator checking membership in a set.
|
||||||
|
|
||||||
|
```pascaligo group=a
|
||||||
|
const contains_3 : bool = my_set contains 3
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=a
|
||||||
|
let contains_3 : bool = Set.mem 3 my_set
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=a
|
||||||
|
let contains_3 : bool = Set.mem (3, my_set);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
### Cardinal
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=a
|
||||||
|
const set_size : nat = size (my_set)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=a
|
||||||
|
let set_size : nat = Set.size my_set
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=a
|
||||||
|
let set_size : nat = Set.size (my_set);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
### Adding or Removing from a Set
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
In PascaLIGO, there are two ways to update a set. Either we create a
|
||||||
|
new set from the given one, or we modify it in-place. First, let us
|
||||||
|
consider the former:
|
||||||
|
|
||||||
|
```pascaligo group=a
|
||||||
|
const larger_set : set (int) = set_add (4, my_set)
|
||||||
|
|
||||||
|
const smaller_set : set (int) = set_remove (3, my_set)
|
||||||
|
```
|
||||||
|
|
||||||
|
If we are in a block, we can use an instruction to modify the set
|
||||||
|
bound to a given variable. This is called a *patch*. It is only
|
||||||
|
possible to add elements by means of a patch, not remove any: it is
|
||||||
|
the union of two sets.
|
||||||
|
|
||||||
|
In the following example, the parameter set `s` of function `update`
|
||||||
|
is augmented (as the `with s` shows) to include `4` and `7`, that is,
|
||||||
|
this instruction is equivalent to perform the union of two sets, one
|
||||||
|
that is modified in-place, and the other given as a literal
|
||||||
|
(extensional definition).
|
||||||
|
|
||||||
|
``pascaligo group=a
|
||||||
|
function update (var s : set (int)) : set (int) is block {
|
||||||
|
patch s with set [4; 7]
|
||||||
|
} with s
|
||||||
|
|
||||||
|
const new_set : set (int) = update (my_set)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, we update a given set by creating another one, with or
|
||||||
|
without some elements.
|
||||||
|
|
||||||
|
```cameligo group=a
|
||||||
|
let larger_set : int set = Set.add 4 my_set
|
||||||
|
|
||||||
|
let smaller_set : int set = Set.remove 3 my_set
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
In ReasonLIGO, we update a given set by creating another one, with or
|
||||||
|
without some elements.
|
||||||
|
|
||||||
|
```reasonligo group=a
|
||||||
|
let larger_set : set (int) = Set.add (4, my_set);
|
||||||
|
|
||||||
|
let smaller_set : set (int) = Set.remove (3, my_set);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
### Folding over a Set
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
Given a set, we may want to apply a function in turn to all the
|
||||||
|
elements it contains, while accumulating some value which is returned
|
||||||
|
at the end. This is a *fold operation*. In the following example, we
|
||||||
|
sum up all the elements of the set `my_set` defined above.
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
In PascaLIGO, the folded function takes the accumulator first and the
|
||||||
|
(current) set element second. The predefined fold is called `set_fold`.
|
||||||
|
|
||||||
|
```pascaligo group=a
|
||||||
|
function sum (const acc : int; const i : int): int is acc + i
|
||||||
|
|
||||||
|
const sum_of_elements : int = set_fold (sum, my_set, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to use a *loop* over a set as well.
|
||||||
|
|
||||||
|
```pascaligo group=a
|
||||||
|
function loop (const s : set (int)) : int is block {
|
||||||
|
var sum : int := 0;
|
||||||
|
for element in set s block {
|
||||||
|
sum := sum + element
|
||||||
|
}
|
||||||
|
} with sum
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
In CameLIGO, the predefined fold over sets is called `Set.fold`.
|
||||||
|
|
||||||
|
```cameligo group=a
|
||||||
|
let sum (acc, i : int * int) : int = acc + i
|
||||||
|
|
||||||
|
let sum_of_elements : int = Set.fold sum my_set 0
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
`
|
||||||
|
In ReasonLIGO, the predefined fold over sets is called `Set.fold`.
|
||||||
|
|
||||||
|
``reasonligo group=a
|
||||||
|
let sum = ((acc, i) : (int, int)) : int => acc + i;
|
||||||
|
|
||||||
|
let sum_of_elements : int = Set.fold (sum, my_set, 0);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
type magnitude is Small | Large // See variant types
|
||||||
|
|
||||||
|
function compare (const n : nat) : magnitude is
|
||||||
|
if n < 10n then Small (Unit) else Large (Unit)
|
@ -0,0 +1,4 @@
|
|||||||
|
type magnitude = Small | Large // See variant types
|
||||||
|
|
||||||
|
let compare (n : nat) : magnitude =
|
||||||
|
if n < 10n then Small else Large
|
@ -0,0 +1,4 @@
|
|||||||
|
type magnitude = | Small | Large; // See variant types
|
||||||
|
|
||||||
|
let compare = (n : nat) : magnitude =>
|
||||||
|
if (n < 10n) { Small; } else { Large; };
|
@ -1,4 +0,0 @@
|
|||||||
const min_age: nat = 16n;
|
|
||||||
|
|
||||||
function is_adult(const age: nat): bool is
|
|
||||||
if (age > min_age) then True else False
|
|
@ -1,4 +0,0 @@
|
|||||||
function add(const a: int; const b: int): int is
|
|
||||||
begin
|
|
||||||
const result: int = a + b;
|
|
||||||
end with result;
|
|
@ -1,3 +1,4 @@
|
|||||||
const increment : (int -> int) = (function (const i : int) : int is i + 1);
|
function increment (const b : int) : int is
|
||||||
// a = 2
|
(function (const a : int) : int is a + 1) (b)
|
||||||
const a: int = increment(1);
|
|
||||||
|
const a : int = increment (1); // a = 2
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
let increment (b : int) : int = (fun (a : int) -> a + 1) b
|
||||||
|
let a : int = increment 1 // a = 2
|
@ -0,0 +1,2 @@
|
|||||||
|
let increment = (b : int) : int => ((a : int) : int => a + 1)(b);
|
||||||
|
let a : int = increment (1); // a = 2
|
@ -1 +1 @@
|
|||||||
function add(const a: int; const b: int): int is a + b
|
function add (const a: int; const b: int): int is a + b
|
@ -0,0 +1 @@
|
|||||||
|
let add (a : int) (b : int) : int = a + b
|
@ -0,0 +1 @@
|
|||||||
|
let add = ((a, b): (int, int)) : int => a + b;
|
@ -0,0 +1,3 @@
|
|||||||
|
let add (a, b : int * int) : int = a + b // Uncurried
|
||||||
|
let add_curry (a : int) (b : int) : int = add (a,b) // Curried
|
||||||
|
let increment (b : int) : int = add_curry 1 // Partial application
|
22
gitlab-pages/docs/language-basics/src/loops/collection.ligo
Normal file
22
gitlab-pages/docs/language-basics/src/loops/collection.ligo
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
function sum_list (var l : list (int)) : int is block {
|
||||||
|
var total : int := 0;
|
||||||
|
for i in list l block {
|
||||||
|
total := total + i
|
||||||
|
}
|
||||||
|
} with total
|
||||||
|
|
||||||
|
function sum_set (var s : set (int)) : int is block {
|
||||||
|
var total : int := 0;
|
||||||
|
for i in set s block {
|
||||||
|
total := total + i
|
||||||
|
}
|
||||||
|
} with total
|
||||||
|
|
||||||
|
function sum_map (var m : map (string, int)) : string * int is block {
|
||||||
|
var string_total : string := "";
|
||||||
|
var int_total : int := 0;
|
||||||
|
for key -> value in map m block {
|
||||||
|
string_total := string_total ^ key;
|
||||||
|
int_total := int_total + value
|
||||||
|
}
|
||||||
|
} with (string_total, int_total)
|
14
gitlab-pages/docs/language-basics/src/loops/gcd.ligo
Normal file
14
gitlab-pages/docs/language-basics/src/loops/gcd.ligo
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
function gcd (var x : nat; var y : nat) : nat is block {
|
||||||
|
if x < y then
|
||||||
|
block {
|
||||||
|
const z : nat = x;
|
||||||
|
x := y; y := z
|
||||||
|
}
|
||||||
|
else skip;
|
||||||
|
var r : nat := 0n;
|
||||||
|
while y =/= 0n block {
|
||||||
|
r := x mod y;
|
||||||
|
x := y;
|
||||||
|
y := r
|
||||||
|
}
|
||||||
|
} with x
|
7
gitlab-pages/docs/language-basics/src/loops/gcd.mligo
Normal file
7
gitlab-pages/docs/language-basics/src/loops/gcd.mligo
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
let iter (x,y : nat * nat) : bool * (nat * nat) =
|
||||||
|
if y = 0n then stop (x,y) else continue (y, x mod y)
|
||||||
|
|
||||||
|
let gcd (x,y : nat * nat) : nat =
|
||||||
|
let x,y = if x < y then y,x else x,y in
|
||||||
|
let x,y = Loop.fold_while iter (x,y)
|
||||||
|
in x
|
7
gitlab-pages/docs/language-basics/src/loops/gcd.religo
Normal file
7
gitlab-pages/docs/language-basics/src/loops/gcd.religo
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) =>
|
||||||
|
if (y == 0n) { stop ((x,y)); } else { continue ((y, x mod y)); };
|
||||||
|
|
||||||
|
let gcd = ((x,y) : (nat, nat)) : nat =>
|
||||||
|
let (x,y) = if (x < y) { (y,x); } else { (x,y); };
|
||||||
|
let (x,y) = Loop.fold_while (iter, (x,y));
|
||||||
|
x;
|
6
gitlab-pages/docs/language-basics/src/loops/sum.ligo
Normal file
6
gitlab-pages/docs/language-basics/src/loops/sum.ligo
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
function sum (var n : nat) : int is block {
|
||||||
|
var acc : int := 0;
|
||||||
|
for i := 1 to int (n) block {
|
||||||
|
acc := acc + i
|
||||||
|
}
|
||||||
|
} with acc
|
@ -0,0 +1,10 @@
|
|||||||
|
type point is record [x : int; y : int; z : int]
|
||||||
|
type vector is record [dx : int; dy : int]
|
||||||
|
|
||||||
|
const origin : point = record [x = 0; y = 0; z = 0]
|
||||||
|
|
||||||
|
function xy_translate (var p : point; const vec : vector) : point is
|
||||||
|
block {
|
||||||
|
patch p with record [x = p.x + vec.dx];
|
||||||
|
patch p with record [y = p.y + vec.dy]
|
||||||
|
} with p
|
@ -0,0 +1,9 @@
|
|||||||
|
type point is record [x : int; y : int; z : int]
|
||||||
|
type vector is record [dx : int; dy : int]
|
||||||
|
|
||||||
|
const origin : point = record [x = 0; y = 0; z = 0]
|
||||||
|
|
||||||
|
function xy_translate (var p : point; const vec : vector) : point is
|
||||||
|
block {
|
||||||
|
patch p with record [x = p.x + vec.dx; y = p.y + vec.dy]
|
||||||
|
} with p
|
@ -0,0 +1,8 @@
|
|||||||
|
type point is record [x : int; y : int; z : int]
|
||||||
|
type vector is record [dx : int; dy : int]
|
||||||
|
|
||||||
|
const origin : point = record [x = 0; y = 0; z = 0]
|
||||||
|
|
||||||
|
function xy_translate (var p : point; const vec : vector) : point is block {
|
||||||
|
const p : point = p with record [x = p.x + vec.dx; y = p.y + vec.dy]
|
||||||
|
} with p
|
@ -0,0 +1,7 @@
|
|||||||
|
type point is record [x : int; y : int; z : int]
|
||||||
|
type vector is record [dx : int; dy : int]
|
||||||
|
|
||||||
|
const origin : point = record [x = 0; y = 0; z = 0]
|
||||||
|
|
||||||
|
function xy_translate (var p : point; const vec : vector) : point is
|
||||||
|
p with record [x = p.x + vec.dx; y = p.y + vec.dy]
|
@ -0,0 +1,7 @@
|
|||||||
|
type point = {x : int; y : int; z : int}
|
||||||
|
type vector = {dx : int; dy : int}
|
||||||
|
|
||||||
|
let origin : point = {x = 0; y = 0; z = 0}
|
||||||
|
|
||||||
|
let xy_translate (p, vec : point * vector) : point =
|
||||||
|
{p with x = p.x + vec.dx; y = p.y + vec.dy}
|
@ -0,0 +1,7 @@
|
|||||||
|
type point = {x : int, y : int, z : int};
|
||||||
|
type vector = {dx : int, dy : int};
|
||||||
|
|
||||||
|
let origin : point = {x : 0, y : 0, z : 0};
|
||||||
|
|
||||||
|
let xy_translate = ((p, vec) : (point, vector)) : point =>
|
||||||
|
{...p, x : p.x + vec.dx, y : p.y + vec.dy};
|
@ -1,13 +0,0 @@
|
|||||||
// int + int produces int
|
|
||||||
const a: int = 5 + 10;
|
|
||||||
// nat + int produces int
|
|
||||||
const b: int = 5n + 10;
|
|
||||||
// tez + tez produces tez
|
|
||||||
const c: tez = 5mutez + 10mutez;
|
|
||||||
// you can't add tez + int or tez + nat, this won't compile
|
|
||||||
// const d: tez = 5mutez + 10n;
|
|
||||||
// two nats produce a nat
|
|
||||||
const e: nat = 5n + 10n;
|
|
||||||
// nat + int produces an int, this won't compile
|
|
||||||
// const f: nat = 5n + 10;
|
|
||||||
const g: int = 1_000_000;
|
|
@ -1,2 +0,0 @@
|
|||||||
const a: int = int(1n);
|
|
||||||
const b: nat = abs(1);
|
|
@ -1,5 +0,0 @@
|
|||||||
const a: int = 10 / 3;
|
|
||||||
const b: nat = 10n / 3n;
|
|
||||||
const c: nat = 10mutez / 3mutez;
|
|
||||||
|
|
||||||
const d: int = 10 / 5 / 2 * 5;
|
|
@ -1 +0,0 @@
|
|||||||
const its_a_nat: option(nat) = is_nat(1)
|
|
@ -1,3 +0,0 @@
|
|||||||
const a: int = 5 * 5;
|
|
||||||
const b: nat = 5n * 5n;
|
|
||||||
const c: tez = 5n * 5mutez;
|
|
@ -1,6 +0,0 @@
|
|||||||
const a: int = 5 - 10;
|
|
||||||
// substraction of two nats, yields an int
|
|
||||||
const b: int = 5n - 2n;
|
|
||||||
// won't compile, result is an int, not a nat
|
|
||||||
// const c: nat = 5n - 2n;
|
|
||||||
const d: tez = 5mutez - 1mutez;
|
|
@ -0,0 +1,12 @@
|
|||||||
|
const my_list : list (int) = list [1; 2; 2] // The head is 1
|
||||||
|
|
||||||
|
const larger_list : int_list = 5 # my_list
|
||||||
|
|
||||||
|
function increment (const i : int): int is i + 1
|
||||||
|
|
||||||
|
// Creates a new list with all elements incremented by 1
|
||||||
|
const plus_one : list (int) = list_map (increment, larger_list);
|
||||||
|
|
||||||
|
function sum (const acc : int; const i : int): int is acc + i
|
||||||
|
|
||||||
|
const sum_of_elements : int = list_fold (sum, my_list, 0)
|
@ -0,0 +1,28 @@
|
|||||||
|
type int_set is set (int)
|
||||||
|
|
||||||
|
const my_set : int_set = set [3; 2; 2; 1]
|
||||||
|
|
||||||
|
const contains_3 : bool = my_set contains 3
|
||||||
|
|
||||||
|
const set_size : nat = size (my_set)
|
||||||
|
|
||||||
|
const larger_set : int_set = set_add (4, my_set)
|
||||||
|
|
||||||
|
const smaller_set : int_set = set_remove (3, my_set)
|
||||||
|
|
||||||
|
function update (var s : set (int)) : set (int) is block {
|
||||||
|
patch s with set [4; 7]
|
||||||
|
} with s
|
||||||
|
|
||||||
|
const new_set : set (int) = update (my_set)
|
||||||
|
|
||||||
|
function sum (const acc : int; const i : int): int is acc + i
|
||||||
|
|
||||||
|
const sum_of_elements : int = set_fold (sum, my_set, 0)
|
||||||
|
|
||||||
|
function loop (const s : set (int)) : int is block {
|
||||||
|
var sum : int := 0;
|
||||||
|
for element in set s block {
|
||||||
|
sum := sum + element
|
||||||
|
}
|
||||||
|
} with sum
|
@ -0,0 +1,16 @@
|
|||||||
|
type int_set = int set
|
||||||
|
|
||||||
|
let my_set : int_set =
|
||||||
|
Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set))))
|
||||||
|
|
||||||
|
let contains_3 : bool = Set.mem 3 my_set
|
||||||
|
|
||||||
|
let set_size : nat = Set.size my_set
|
||||||
|
|
||||||
|
let larger_set : int_set = Set.add 4 my_set
|
||||||
|
|
||||||
|
let smaller_set : int_set = Set.remove 3 my_set
|
||||||
|
|
||||||
|
let sum (acc, i : int * int) : int = acc + i
|
||||||
|
|
||||||
|
let sum_of_elements : int = Set.fold sum my_set 0
|
@ -0,0 +1,16 @@
|
|||||||
|
type int_set = set (int);
|
||||||
|
|
||||||
|
let my_set : int_set =
|
||||||
|
Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int)))));
|
||||||
|
|
||||||
|
let contains_3 : bool = Set.mem (3, my_set);
|
||||||
|
|
||||||
|
let set_size : nat = Set.size (my_set);
|
||||||
|
|
||||||
|
let larger_set : int_set = Set.add (4, my_set);
|
||||||
|
|
||||||
|
let smaller_set : int_set = Set.remove (3, my_set);
|
||||||
|
|
||||||
|
let sum = ((acc, i) : (int, int)) : int => acc + i;
|
||||||
|
|
||||||
|
let sum_of_elements : int = Set.fold (sum, my_set, 0);
|
@ -1 +0,0 @@
|
|||||||
const a: string = string_concat("Hello ", "World");
|
|
@ -1,2 +0,0 @@
|
|||||||
type animalBreed is string;
|
|
||||||
const dogBreed : animalBreed = "Saluki";
|
|
@ -1,4 +0,0 @@
|
|||||||
type int_map is map(int, int);
|
|
||||||
function get_first(const int_map: int_map): option(int) is int_map[1]
|
|
||||||
// empty map needs a type annotation
|
|
||||||
const first: option(int) = get_first(((map end) : int_map ));
|
|
@ -1,19 +0,0 @@
|
|||||||
// alias two types
|
|
||||||
type account is address;
|
|
||||||
type numberOfTransactions is nat;
|
|
||||||
// accountData consists of a record with two fields (balance, numberOfTransactions)
|
|
||||||
type accountData is record
|
|
||||||
balance: tez;
|
|
||||||
numberOfTransactions: numberOfTransactions;
|
|
||||||
end
|
|
||||||
// our ledger / accountBalances is a map of account <-> accountData
|
|
||||||
type accountBalances is map(account, accountData);
|
|
||||||
|
|
||||||
// pseudo-JSON representation of our map
|
|
||||||
// { "tz1...": {balance: 10mutez, numberOfTransactions: 5n} }
|
|
||||||
const ledger: accountBalances = map
|
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> record
|
|
||||||
balance = 10mutez;
|
|
||||||
numberOfTransactions = 5n;
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,6 +1,7 @@
|
|||||||
// accountBalances is a simple type, a map of address <-> tez
|
// The type accountBalances denotes maps from addresses to tez
|
||||||
type accountBalances is map(address, tez);
|
|
||||||
|
|
||||||
const ledger: accountBalances = map
|
type account_balances is map (address, tez)
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> 10mutez
|
|
||||||
end
|
const ledger : account_balances =
|
||||||
|
map
|
||||||
|
[("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez]
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
type coin is Head | Tail
|
||||||
|
|
||||||
|
function flip (const c : coin) : coin is
|
||||||
|
case c of
|
||||||
|
Head -> Tail (Unit) // Unit needed because of a bug
|
||||||
|
| Tail -> Head (Unit) // Unit needed because of a bug
|
||||||
|
end
|
@ -1,7 +1,4 @@
|
|||||||
// won't work, use const for global values instead
|
function add (const a : int; const b : int) : int is
|
||||||
// var four: int = 4;
|
block {
|
||||||
|
var c : int := a + b
|
||||||
function add(const a: int; const b: int) : int is
|
} with c
|
||||||
block {
|
|
||||||
var c : int := a + b;
|
|
||||||
} with c
|
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
let add (a : int) (b : int) : int =
|
||||||
|
let c : int = a + b in c
|
@ -1,4 +1,4 @@
|
|||||||
let add = (a: int, b: int): int => {
|
let add = (a: int, b: int) : int => {
|
||||||
let c: int = a + b;
|
let c: int = a + b;
|
||||||
c;
|
c;
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
let age : int = 25
|
@ -3,22 +3,20 @@ id: strings
|
|||||||
title: Strings
|
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"
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```
|
```
|
||||||
let a: string = "Hello Alice"
|
let a : string = "Hello Alice"
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo
|
||||||
let a: string = "Hello Alice";
|
let a : string = "Hello Alice";
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
@ -30,77 +28,74 @@ let a: string = "Hello Alice";
|
|||||||
Strings can be concatenated using the `^` operator.
|
Strings can be concatenated using the `^` operator.
|
||||||
|
|
||||||
```pascaligo
|
```pascaligo
|
||||||
const name: string = "Alice";
|
const name : string = "Alice"
|
||||||
const greeting: string = "Hello";
|
const greeting : string = "Hello"
|
||||||
// Hello Alice
|
const full_greeting : string = greeting ^ " " ^ name
|
||||||
const full_greeting: string = greeting ^ " " ^ name;
|
|
||||||
// Hello Alice! (alternatively)
|
|
||||||
const full_greeting_exclamation: string = string_concat(full_greeting, "!");
|
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
Strings can be concatenated using the `^` operator.
|
Strings can be concatenated using the `^` operator.
|
||||||
|
|
||||||
```cameligo
|
```cameligo
|
||||||
let name: string = "Alice"
|
let name : string = "Alice"
|
||||||
let greeting: string = "Hello"
|
let greeting : string = "Hello"
|
||||||
let full_greeting: string = greeting ^ " " ^ name
|
let full_greeting : string = greeting ^ " " ^ name
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
Strings can be concatenated using the `++` operator.
|
Strings can be concatenated using the `++` operator.
|
||||||
|
|
||||||
```reasonligo
|
```reasonligo
|
||||||
let name: string = "Alice";
|
let name : string = "Alice";
|
||||||
let greeting: string = "Hello";
|
let greeting : string = "Hello";
|
||||||
let full_greeting: string = greeting ++ " " ++ name;
|
let full_greeting : string = greeting ++ " " ++ name;
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Slicing strings
|
## Slicing strings
|
||||||
|
|
||||||
Strings can be sliced using the syntax specific built-in 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"
|
||||||
// slice = "A"
|
const slice : string = string_slice (0n, 1n, name)
|
||||||
const slice: string = string_slice(0n, 1n, name);
|
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo
|
||||||
let name: string = "Alice"
|
let name : string = "Alice"
|
||||||
let slice: string = String.slice 0n 1n name
|
let slice : string = String.slice 0n 1n name
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo
|
||||||
let name: string = "Alice";
|
let name : string = "Alice";
|
||||||
let slice: string = String.slice(0n, 1n, name);
|
let slice : string = String.slice (0n, 1n, name);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
> ⚠️ Notice that the `offset` and slice `length` are `nats`
|
> ⚠️ Notice that the `offset` and slice `length` are natural numbers
|
||||||
|
> (`nat`).
|
||||||
|
|
||||||
## Aquiring the length of a string
|
## Aquiring the length of a string
|
||||||
|
|
||||||
The length of a string can be found using the syntax specific built-in function:
|
The length of a string can be found using a built-in function:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo
|
||||||
const name: string = "Alice";
|
const name : string = "Alice"
|
||||||
// length = 5
|
// length = 5
|
||||||
const length: nat = size(name);
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo
|
||||||
let name: string = "Alice";
|
let name : string = "Alice";
|
||||||
let length: nat = String.size(name);
|
let length : nat = String.size (name);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,38 +3,45 @@ id: tezos-specific
|
|||||||
title: Tezos Domain-Specific Operations
|
title: Tezos Domain-Specific Operations
|
||||||
---
|
---
|
||||||
|
|
||||||
LIGO is a language for writing Tezos smart contracts. It would be a little odd if
|
LIGO is a programming language for writing Tezos smart contracts. It
|
||||||
it didn't have any Tezos specific functions. This page will tell you about them.
|
would be a little odd if it did not have any Tezos specific
|
||||||
|
functions. This page will tell you about them.
|
||||||
|
|
||||||
## Pack and Unpack
|
## Pack and unpack
|
||||||
|
|
||||||
Michelson provides the `PACK` and `UNPACK` instructions for data serialization.
|
Michelson provides the `PACK` and `UNPACK` instructions for data
|
||||||
`PACK` converts Michelson data structures to a binary format, and `UNPACK`
|
serialization. The instruction `PACK` converts Michelson data
|
||||||
reverses it. This functionality can be accessed from within LIGO.
|
structures into a binary format, and `UNPACK` reverses that
|
||||||
|
transformation. This functionality can be accessed from within LIGO.
|
||||||
|
|
||||||
> ⚠️ `PACK` and `UNPACK` are features of Michelson that are intended to be used by people that really know what they're doing. There are several failure cases (such as `UNPACK`ing a lambda from an untrusted source), most of which are beyond the scope of this document. Don't use these functions without doing your homework first.
|
> ⚠️ `PACK` and `UNPACK` are Michelson instructions that are intended
|
||||||
|
> to be used by people that really know what they are doing. There are
|
||||||
|
> several risks and failure cases, such as unpacking a lambda from an
|
||||||
|
> untrusted source, and most of which are beyond the scope of this
|
||||||
|
> document. Do not use these functions without doing your homework
|
||||||
|
> first.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
function id_string (const p : string) : option(string) is block {
|
function id_string (const p : string) : option (string) is block {
|
||||||
const packed : bytes = bytes_pack(p) ;
|
const packed : bytes = bytes_pack (p)
|
||||||
} with (bytes_unpack(packed): option(string))
|
} with (bytes_unpack (packed): option (string))
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=a
|
||||||
let id_string (p: string) : string option =
|
let id_string (p : string) : string option =
|
||||||
let packed: bytes = Bytes.pack p in
|
let packed: bytes = Bytes.pack p in
|
||||||
((Bytes.unpack packed): string option)
|
(Bytes.unpack packed : string option)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=a
|
||||||
let id_string = (p: string) : option(string) => {
|
let id_string = (p : string) : option (string) => {
|
||||||
let packed : bytes = Bytes.pack(p);
|
let packed : bytes = Bytes.pack (p);
|
||||||
((Bytes.unpack(packed)): option(string));
|
(Bytes.unpack(packed) : option (string));
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -42,41 +49,35 @@ let id_string = (p: string) : option(string) => {
|
|||||||
|
|
||||||
## Hashing Keys
|
## Hashing Keys
|
||||||
|
|
||||||
It's often desirable to hash a public key. In Michelson, certain data structures
|
It is often desirable to hash a public key. In Michelson, certain data
|
||||||
such as maps will not allow the use of the `key` type. Even if this weren't the case
|
structures such as maps will not allow the use of the `key` type. Even
|
||||||
hashes are much smaller than keys, and storage on blockchains comes at a cost premium.
|
if this were not the case, hashes are much smaller than keys, and
|
||||||
You can hash keys with the `key_hash` type and associated built in function.
|
storage on blockchains comes at a cost premium. You can hash keys an
|
||||||
|
predefined function returning a value of type `key_hash`.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```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 block {
|
||||||
var ret : bool := False ;
|
var ret : bool := False;
|
||||||
var kh2 : key_hash := crypto_hash_key(k2) ;
|
var kh2 : key_hash := crypto_hash_key (k2);
|
||||||
if kh1 = kh2 then ret := True else skip;
|
if kh1 = kh2 then ret := True else skip
|
||||||
} with (ret, kh2)
|
} with (ret, kh2)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=b
|
||||||
let check_hash_key (kh1, k2: key_hash * key) : bool * key_hash =
|
let check_hash_key (kh1, k2 : key_hash * key) : bool * key_hash =
|
||||||
let kh2 : key_hash = Crypto.hash_key k2 in
|
let kh2 : key_hash = Crypto.hash_key k2 in
|
||||||
if kh1 = kh2
|
if kh1 = kh2 then true, kh2 else false, kh2
|
||||||
then (true, kh2)
|
|
||||||
else (false, kh2)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=b
|
||||||
let check_hash_key = ((kh1, k2): (key_hash, key)) : (bool, key_hash) => {
|
let check_hash_key = ((kh1, k2) : (key_hash, key)) : (bool, key_hash) => {
|
||||||
let kh2 : key_hash = Crypto.hash_key(k2);
|
let kh2 : key_hash = Crypto.hash_key(k2);
|
||||||
if (kh1 == kh2) {
|
if (kh1 == kh2) { (true, kh2); } else { (false, kh2); }
|
||||||
(true, kh2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
(false, kh2);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -84,60 +85,66 @@ let check_hash_key = ((kh1, k2): (key_hash, key)) : (bool, key_hash) => {
|
|||||||
|
|
||||||
## Checking Signatures
|
## Checking Signatures
|
||||||
|
|
||||||
Sometimes a contract will want to check that a message has been signed by a
|
Sometimes a contract will want to check that a message has been signed
|
||||||
particular key. For example, a point-of-sale system might want a customer to
|
by a particular key. For example, a point-of-sale system might want a
|
||||||
sign a transaction so it can be processed asynchronously. You can do this in LIGO
|
customer to sign a transaction so it can be processed
|
||||||
using the `key` and `signature` types.
|
asynchronously. You can do this in LIGO using the `key` and
|
||||||
|
`signature` types.
|
||||||
|
|
||||||
> ⚠️ There is no way to *generate* a signed message in LIGO. This is because that would require storing a private key on chain, at which point it isn't very private anymore.
|
> ⚠️ There is no way to *generate* a signed message in LIGO. This is
|
||||||
|
> because that would require storing a private key on chain, at which
|
||||||
|
> point it is not... private anymore.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=c
|
||||||
function check_signature
|
function check_signature
|
||||||
(const pk: key;
|
(const pk : key;
|
||||||
const signed: signature;
|
const signed : signature;
|
||||||
const msg: bytes) : bool
|
const msg : bytes) : bool
|
||||||
is crypto_check(pk, signed, msg)
|
is crypto_check (pk, signed, msg)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=c
|
||||||
let check_signature (pk, signed, msg: key * signature * bytes) : bool =
|
let check_signature (pk, signed, msg : key * signature * bytes) : bool =
|
||||||
Crypto.check pk signed msg
|
Crypto.check pk signed msg
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=c
|
||||||
let check_signature = ((pk, signed, msg): (key, signature, bytes)) : bool => {
|
let check_signature =
|
||||||
Crypto.check(pk, signed, msg);
|
((pk, signed, msg) : (key, signature, bytes)) : bool => {
|
||||||
|
Crypto.check (pk, signed, msg);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Getting The Contract's Own Address
|
|
||||||
|
|
||||||
Often you want to get the address of the contract being executed. You can do it with
|
## Getting the contract's own address
|
||||||
`self_address`.
|
|
||||||
|
|
||||||
> ⚠️ Due to limitations in Michelson, self_address in a contract is only allowed at the entry-point level. Using it in a utility function will cause an error.
|
Often you want to get the address of the contract being executed. You
|
||||||
|
can do it with `self_address`.
|
||||||
|
|
||||||
|
> ⚠️ Due to limitations in Michelson, `self_address` in a contract is
|
||||||
|
> only allowed at the entry-point level (a.k.a top-level). Using it in
|
||||||
|
> an auxiliaru function will cause an error.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=d
|
||||||
const current_addr : address = self_address;
|
const current_addr : address = self_address
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=d
|
||||||
let current_addr : address = Current.self_address
|
let current_addr : address = Current.self_address
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=d
|
||||||
let current_addr : address = Current.self_address;
|
let current_addr : address = Current.self_address;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -3,163 +3,189 @@ id: types
|
|||||||
title: Types
|
title: Types
|
||||||
---
|
---
|
||||||
|
|
||||||
LIGO is strongly and statically typed. This means that the compiler checks your program at compilation time and makes sure there won't be any type related runtime errors. LIGO types are built on top of Michelson's type system.
|
LIGO is strongly and statically typed. This means that the compiler
|
||||||
|
checks your program at compilation time and, if it passes the tests,
|
||||||
|
this ensures that there will be no runtime error due to wrong
|
||||||
|
assumptions on the data. This is called *type checking*.
|
||||||
|
|
||||||
|
LIGO types are built on top of Michelson's type system.
|
||||||
|
|
||||||
## Built-in types
|
## Built-in types
|
||||||
|
|
||||||
For quick referrence, you can find all the built-in types [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L35).
|
For quick reference, you can find all the built-in types [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L35).
|
||||||
|
|
||||||
## Type aliases
|
## Type aliases
|
||||||
|
|
||||||
Type aliasing is great for creating a readable / maintainable smart contract. One well typed type/variable is worth a thousand words. For example we can choose to *alias* a string as an animal breed - this will allow us to comunicate our intent with added clarity.
|
*Type aliasing* consists in renaming a given type, when the context
|
||||||
|
calls for a more precise name. This increases readability and
|
||||||
|
maintainability of your smart contracts. For example we can choose to
|
||||||
|
alias a string type as an animal breed - this will allow us to
|
||||||
|
comunicate our intent with added clarity.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
type animalBreed is string;
|
type breed is string
|
||||||
const dogBreed : animalBreed = "Saluki";
|
const dog_breed : breed = "Saluki"
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
```cameligo
|
```cameligo group=a
|
||||||
type animal_breed = string
|
type breed = string
|
||||||
let dog_breed: animal_breed = "Saluki"
|
let dog_breed : breed = "Saluki"
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
```reasonligo
|
```reasonligo group=a
|
||||||
type animal_breed = string;
|
type breed = string;
|
||||||
let dog_breed: animal_breed = "Saluki";
|
let dog_breed : breed = "Saluki";
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
> Types in LIGO are `structural`, which means that `animalBreed`/`animal_breed` and `string` are interchangable and are considered equal.
|
> The above type definitions are aliases, which means that `breed` and
|
||||||
|
> `string` are interchangable in all contexts.
|
||||||
|
|
||||||
## Simple types
|
## Simple types
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo group=b
|
||||||
// accountBalances is a simple type, a map of address <-> tez
|
// The type accountBalances denotes maps from addresses to tez
|
||||||
type accountBalances is map(address, tez);
|
|
||||||
|
type account_balances is map (address, tez)
|
||||||
|
|
||||||
|
const ledger : account_balances =
|
||||||
|
map
|
||||||
|
[("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez]
|
||||||
|
|
||||||
const ledger: accountBalances = map
|
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> 10mutez
|
|
||||||
end
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=b
|
||||||
// account_balances is a simple type, a map of address <-> tez
|
// The type account_balances denotes maps from addresses to tez
|
||||||
|
|
||||||
type account_balances = (address, tez) map
|
type account_balances = (address, tez) map
|
||||||
|
|
||||||
let ledger: account_balances = Map.literal
|
let ledger : account_balances =
|
||||||
[(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), 10mutez)]
|
Map.literal
|
||||||
|
[(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), 10mutez)]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=b
|
||||||
(* account_balances is a simple type, a map of address <-> tez *)
|
// The type account_balances denotes maps from addresses to tez
|
||||||
type account_balances = map(address, tez);
|
|
||||||
|
type account_balances = map (address, tez);
|
||||||
|
|
||||||
let ledger: account_balances =
|
let ledger: account_balances =
|
||||||
Map.literal([
|
Map.literal
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, 10mutez)
|
([("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, 10mutez)]);
|
||||||
]);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Composed types
|
## Structured types
|
||||||
|
|
||||||
Often contracts require complex data structures, which in turn require well-typed storage or functions to work with. LIGO offers a simple way to compose simple types into larger & more expressive composed types.
|
Often contracts require complex data structures, which in turn require
|
||||||
|
well-typed storage or functions to work with. LIGO offers a simple way
|
||||||
|
to compose simple types into *structured types*.
|
||||||
|
|
||||||
In the example below you can see the definition of data types for a ledger that keeps the balance and number of previous transactions for a given account.
|
The first of those structured types is the *record*, which aggregates
|
||||||
|
types as *fields* and index them with a *field name*. In the example
|
||||||
|
below you can see the definition of data types for a ledger that keeps
|
||||||
|
the balance and number of previous transactions for a given account.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo group=c
|
||||||
// alias two types
|
// Type aliasing
|
||||||
type account is address;
|
type account is address
|
||||||
type numberOfTransactions is nat;
|
type number_of_transactions is nat
|
||||||
// accountData consists of a record with two fields (balance, numberOfTransactions)
|
|
||||||
type accountData is record
|
|
||||||
balance: tez;
|
|
||||||
numberOfTransactions: numberOfTransactions;
|
|
||||||
end
|
|
||||||
// our ledger / accountBalances is a map of account <-> accountData
|
|
||||||
type accountBalances is map(account, accountData);
|
|
||||||
|
|
||||||
// pseudo-JSON representation of our map
|
// The type account_data is a record with two fields.
|
||||||
// { "tz1...": {balance: 10mutez, numberOfTransactions: 5n} }
|
type account_data is record [
|
||||||
const ledger: accountBalances = map
|
balance : tez;
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> record
|
transactions : number_of_transactions
|
||||||
balance = 10mutez;
|
]
|
||||||
numberOfTransactions = 5n;
|
|
||||||
end
|
// A ledger is a map from accounts to account_data
|
||||||
end
|
type ledger is map (account, account_data)
|
||||||
|
|
||||||
|
const my_ledger : ledger = map [
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) ->
|
||||||
|
record [
|
||||||
|
balance = 10mutez;
|
||||||
|
transactions = 5n
|
||||||
|
]
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=c
|
||||||
(* alias two types *)
|
// Type aliasing
|
||||||
type account = address
|
type account = address
|
||||||
type number_of_transactions = nat
|
type number_of_transactions = nat
|
||||||
(* account_data consists of a record with two fields (balance, number_of_transactions) *)
|
// The type account_data is a record with two fields.
|
||||||
type account_data = {
|
type account_data = {
|
||||||
balance: tez;
|
balance : tez;
|
||||||
number_of_transactions: number_of_transactions;
|
transactions : number_of_transactions
|
||||||
}
|
}
|
||||||
(* our ledger / account_balances is a map of account <-> account_data *)
|
// A ledger is a map from accounts to account_data
|
||||||
type account_balances = (account, account_data) map
|
type ledger = (account, account_data) map
|
||||||
|
|
||||||
// pseudo-JSON representation of our map
|
let my_ledger : ledger = Map.literal
|
||||||
// {"tz1...": {balance: 10mutez, number_of_transactions: 5n}}
|
[(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address),
|
||||||
let ledger: account_balances = Map.literal
|
{balance = 10mutez; transactions = 5n})]
|
||||||
[(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address),
|
|
||||||
{balance = 10mutez;
|
|
||||||
number_of_transactions = 5n;}
|
|
||||||
)]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=c
|
||||||
(* alias two types *)
|
// Type aliasing
|
||||||
type account = address;
|
type account = address;
|
||||||
type number_of_transactions = nat;
|
type number_of_transactions = nat;
|
||||||
(* account_data consists of a record with two fields (balance, number_of_transactions) *)
|
|
||||||
|
// The type account_data is a record with two fields.
|
||||||
type account_data = {
|
type account_data = {
|
||||||
balance: tez,
|
balance : tez,
|
||||||
number_of_transactions,
|
transactions : number_of_transactions
|
||||||
};
|
};
|
||||||
(* our ledger / account_balances is a map of account <-> account_data *)
|
|
||||||
type account_balances = map(account, account_data);
|
|
||||||
|
|
||||||
(* pseudo-JSON representation of our map
|
// A ledger is a map from accounts to account_data
|
||||||
{"tz1...": {balance: 10mutez, number_of_transactions: 5n}} *)
|
type ledger = map (account, account_data);
|
||||||
let ledger: account_balances =
|
|
||||||
|
let my_ledger : ledger =
|
||||||
Map.literal([
|
Map.literal([
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address,
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address,
|
||||||
{balance: 10mutez, number_of_transactions: 5n})
|
{balance: 10mutez, transactions: 5n})]);
|
||||||
]);
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The structured types which are dual to records are the *variant types*
|
||||||
|
and they are described in the section about *pattern matching*. They
|
||||||
|
are dual because records are a product of types (types are bundled
|
||||||
|
into a record), whereas variant types are a sum of types (they are
|
||||||
|
exclusive to each other).
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Annotations
|
## Annotations
|
||||||
|
|
||||||
In certain cases, type of an expression cannot be properly determined. This can be circumvented by annotating an expression with it's desired type, here's an example:
|
In certain cases, the type of an expression cannot be properly
|
||||||
|
inferred by the compiler. In order to help the type checke, you can
|
||||||
|
annotate an expression with its desired type. Here is an example:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo
|
||||||
type int_map is map(int, int);
|
type int_map is map (int, int)
|
||||||
function get_first(const int_map: int_map): option(int) is int_map[1]
|
|
||||||
// empty map needs a type annotation
|
function get_first (const my_map : int_map): option (int) is my_map[1]
|
||||||
const first: option(int) = get_first(((map end) : int_map ));
|
|
||||||
|
// The empty map always needs a type annotation
|
||||||
|
|
||||||
|
const first : option (int) = get_first (((map end) : int_map))
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,110 +3,162 @@ id: unit-option-pattern-matching
|
|||||||
title: Unit, Option, Pattern matching
|
title: Unit, Option, Pattern matching
|
||||||
---
|
---
|
||||||
|
|
||||||
Optionals are a programing pattern seen in OCaml. Since Michelson and LIGO are both inspired by OCaml, you'll have the *option* to use them in LIGO as well.
|
Optionals are a pervasive programing pattern in OCaml. Since Michelson
|
||||||
|
and LIGO are both inspired by OCaml, *optional types* are available in
|
||||||
|
LIGO as well. Similarly, OCaml features a *unit* type, and LIGO
|
||||||
|
features it as well. Both the option type and the unit types are
|
||||||
|
instances of a more general kind of types: *variant types* (sometimes
|
||||||
|
called *sum types*).
|
||||||
|
|
||||||
## Type unit
|
## The unit type
|
||||||
|
|
||||||
Units in Michelson or LIGO represent *for the lack of better words* - an empty/useless/not needed value.
|
The `unit` type in Michelson or LIGO is a predefined type that
|
||||||
|
contains only one value that carries no information. It is used when
|
||||||
|
no relevant information is required or produced. Here is how it used.
|
||||||
|
|
||||||
Here's how they're defined:
|
> 💡 Units come in handy when we try pattern matching on custom
|
||||||
|
> variants below.
|
||||||
> 💡 Units come in handy when we try pattern matching on custom variants below.
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
|
||||||
const n: unit = Unit;
|
In PascaLIGO, the unique value of the `unit` type is `Unit`.
|
||||||
|
```pascaligo group=a
|
||||||
|
const n : unit = Unit
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
|
||||||
let n: unit = ()
|
In CameLIGO, the unique value of the `unit` type is `()`, following
|
||||||
|
the OCaml convention.
|
||||||
|
```cameligo group=a
|
||||||
|
let n : unit = ()
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
|
||||||
let n: unit = ();
|
In ReasonLIGO, the unique value of the `unit` type is `()`, following
|
||||||
|
the OCaml convention.
|
||||||
|
```reasonligo group=a
|
||||||
|
let n : unit = ();
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Variants
|
## Variant types
|
||||||
|
|
||||||
Variant is a user-defined or a built-in type (in case of optionals) that can be compared to Enum (from javascript).
|
A variant type is a user-defined or a built-in type (in case of
|
||||||
|
options) that defines a type by cases, so a value of a variant type is
|
||||||
|
either this, or that or... The simplest variant type is equivalent to
|
||||||
|
the enumerated types found in Java, C++, JavaScript etc.
|
||||||
|
|
||||||
Here's how to define a new variant type:
|
Here is how we define a coin as being either head or tail (and nothing
|
||||||
|
else):
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo group=b
|
||||||
|
type coin is Head | Tail
|
||||||
|
const head : coin = Head (Unit) // Unit needed because of a bug
|
||||||
|
const tail : coin = Tail (Unit) // Unit needed because of a bug
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=b
|
||||||
|
type coin = Head | Tail
|
||||||
|
let head : coin = Head
|
||||||
|
let tail : coin = Tail
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=b
|
||||||
|
type coin = | Head | Tail;
|
||||||
|
let head : coin = Head;
|
||||||
|
let tail : coin = Tail;
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
The names `Head` and `Tail` in the definition of the type `coin` are
|
||||||
|
called *data constructors*, or *variants*.
|
||||||
|
|
||||||
|
In general, it is interesting for variants to carry some information,
|
||||||
|
and thus go beyond enumerated types. In the following, we show how to
|
||||||
|
define different kinds of users of a system.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=c
|
||||||
type id is nat
|
type id is nat
|
||||||
|
|
||||||
type user is
|
type user is
|
||||||
| Admin of id
|
Admin of id
|
||||||
| Manager of id
|
| Manager of id
|
||||||
|
| Guest
|
||||||
|
|
||||||
|
const u : user = Admin (1000n)
|
||||||
|
const g : user = Guest (Unit) // Unit needed because of a bug
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=c
|
||||||
|
type id = nat
|
||||||
|
|
||||||
|
type user =
|
||||||
|
Admin of id
|
||||||
|
| Manager of id
|
||||||
|
| Guest
|
||||||
|
|
||||||
|
let u : user = Admin 1000n
|
||||||
|
let g : user = Guest
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=c
|
||||||
|
type id = nat;
|
||||||
|
|
||||||
|
type user =
|
||||||
|
| Admin (id)
|
||||||
|
| Manager (id)
|
||||||
| Guest;
|
| Guest;
|
||||||
|
|
||||||
const u: user = Admin(1000n);
|
let u : user = Admin (1000n);
|
||||||
const g: user = Guest(Unit);
|
let g : user = Guest;
|
||||||
```
|
|
||||||
|
|
||||||
<!--CameLIGO-->
|
|
||||||
```cameligo
|
|
||||||
type id = nat
|
|
||||||
type user =
|
|
||||||
| Admin of id
|
|
||||||
| Manager of id
|
|
||||||
| Guest of unit
|
|
||||||
|
|
||||||
let u: user = Admin 1000n
|
|
||||||
let g: user = Guest ()
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
|
||||||
```reasonligo
|
|
||||||
type id = nat;
|
|
||||||
type user =
|
|
||||||
| Admin(id)
|
|
||||||
| Manager(id)
|
|
||||||
| Guest(unit);
|
|
||||||
|
|
||||||
let u: user = Admin(1000n);
|
|
||||||
let g: user = Guest();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
Defining a varient can be extremely useful for building semantically appealing contracts. We'll learn how to use variants for 'logic purposes' shortly.
|
Defining a variant can be extremely useful for building semantically
|
||||||
|
appealing contracts. We will learn how to use variants for "logic
|
||||||
|
purposes"' shortly.
|
||||||
|
|
||||||
## Optional values
|
## Optional values
|
||||||
|
|
||||||
Optionals are a type of built-in variant that can be used to determine if a variable holds a certain value or not. This is especially useful when (for example) your program's state allows for a certain variable value to be empty, like this:
|
The `option` type is a predefined variant type that is used to express
|
||||||
|
whether there is a value of some type or none. This is especially
|
||||||
|
useful when calling a *partial function*, that is, a function that is
|
||||||
|
not defined for some inputs. In that case, the value of the `option`
|
||||||
|
type would be `None`, otherwise `Some (v)`, where `v` is some
|
||||||
|
meaningful value *of any type*. An example in arithmetic is the
|
||||||
|
division operation:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo group=d
|
||||||
type dinner is option(string);
|
function div (const a : nat; const b : nat) : option (nat) is
|
||||||
|
if b = 0n then (None: option (nat)) else Some (a/b)
|
||||||
// stay hungry
|
|
||||||
const p1: dinner = None;
|
|
||||||
// have some hamburgers
|
|
||||||
const p2: dinner = Some("Hamburgers")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=d
|
||||||
type dinner = string option
|
let div (a, b : nat * nat) : nat option =
|
||||||
|
if b = 0n then (None: nat option) else Some (a/b)
|
||||||
let p1: dinner = None
|
|
||||||
let p2: dinner = Some "Hamburgers"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=d
|
||||||
type dinner = option(string);
|
let div = ((a, b) : (nat, nat)) : option (nat) =>
|
||||||
|
if (b == 0n) { (None: option (nat)); } else { Some (a/b); };
|
||||||
let p1: dinner = None;
|
|
||||||
let p2: dinner = Some("Hamburgers");
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
@ -114,38 +166,66 @@ let p2: dinner = Some("Hamburgers");
|
|||||||
|
|
||||||
## Pattern matching
|
## Pattern matching
|
||||||
|
|
||||||
Pattern matching is very similiar to e.g. `switch` in Javascript, and can be used to re-route the program's flow based on a value of a variant.
|
*Pattern matching* is similiar to the `switch` construct in
|
||||||
|
Javascript, and can be used to route the program's control flow based
|
||||||
|
on the value of a variant. Consider for example the definition of a
|
||||||
|
function `flip` that flips a coin, as defined above.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo group=e
|
||||||
type dinner is option(string);
|
type coin is Head | Tail
|
||||||
function is_hungry(const dinner: dinner): bool is block { skip }
|
|
||||||
with (
|
function flip (const c : coin) : coin is
|
||||||
case dinner of
|
case c of
|
||||||
| None -> True
|
Head -> Tail (Unit) // Unit needed because of a bug
|
||||||
| Some(d) -> False
|
| Tail -> Head (Unit) // Unit needed because of a bug
|
||||||
end
|
end
|
||||||
)
|
```
|
||||||
|
|
||||||
|
You can call the function `flip` by using the LIGO compiler like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo
|
||||||
|
flip "(Head (Unit))"
|
||||||
|
# Outputs: Tail(Unit)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=e
|
||||||
type dinner = string option
|
type coin = Head | Tail
|
||||||
let is_hungry (d: dinner) : bool =
|
|
||||||
match d with
|
let flip (c : coin) : coin =
|
||||||
| None -> true
|
match c with
|
||||||
| Some s -> false
|
Head -> Tail
|
||||||
|
| Tail -> Head
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `flip` by using the LIGO compiler like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.mligo
|
||||||
|
flip Head
|
||||||
|
# Outputs: Tail(Unit)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=e
|
||||||
type dinner = option(string);
|
type coin = | Head | Tail;
|
||||||
let is_hungry = (d: dinner): bool =>
|
|
||||||
switch (d) {
|
let flip = (c : coin) : coin =>
|
||||||
| None => true
|
switch (c) {
|
||||||
| Some(s) => false
|
| Head => Tail
|
||||||
|
| Tail => Head
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can call the function `flip` by using the LIGO compiler like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.religo
|
||||||
|
flip Head
|
||||||
|
# Outputs: Tail(Unit)
|
||||||
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,43 +3,48 @@ id: constants-and-variables
|
|||||||
title: Constants & Variables
|
title: Constants & Variables
|
||||||
---
|
---
|
||||||
|
|
||||||
The next building block after types are constants and variables.
|
The next building block after types are *constants* and *variables*.
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
||||||
Constants are immutable by design, which means their values can't be reassigned.
|
Constants are immutable by design, which means their values cannot be
|
||||||
When defining a constant you need to provide a `name`, `type` and a `value`:
|
reassigned. Put in another way, they can be assigned once, at their
|
||||||
|
declaration. When defining a constant you need to provide a `name`,
|
||||||
|
`type` and a `value`:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
const age : int = 25;
|
const age : int = 25
|
||||||
```
|
```
|
||||||
|
|
||||||
You can evaluate the constant definition above using the following CLI command:
|
You can evaluate the constant definition above using the following CLI
|
||||||
|
command:
|
||||||
```shell
|
```shell
|
||||||
ligo evaluate-value -s pascaligo gitlab-pages/docs/language-basics/src/variables-and-constants/const.ligo age
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.ligo age
|
||||||
# Outputs: 25
|
# Outputs: 25
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=a
|
||||||
let age: int = 25
|
let age : int = 25
|
||||||
```
|
```
|
||||||
|
|
||||||
You can evaluate the constant definition above using the following CLI command:
|
You can evaluate the constant definition above using the following CLI
|
||||||
|
command:
|
||||||
```shell
|
```shell
|
||||||
ligo evaluate-value -s cameligo gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo age
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo age
|
||||||
# Outputs: 25
|
# Outputs: 25
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=a
|
||||||
let age: int = 25;
|
let age : int = 25;
|
||||||
```
|
```
|
||||||
|
|
||||||
You can evaluate the constant definition above using the following CLI command:
|
You can evaluate the constant definition above using the following CLI
|
||||||
|
command:
|
||||||
```shell
|
```shell
|
||||||
ligo evaluate-value -s reasonligo gitlab-pages/docs/language-basics/src/variables-and-constants/const.religo age
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.religo age
|
||||||
# Outputs: 25
|
# Outputs: 25
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -50,74 +55,78 @@ ligo evaluate-value -s reasonligo gitlab-pages/docs/language-basics/src/variable
|
|||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
|
|
||||||
Variables, unlike constants, are mutable. They can't be used in a *global scope*, but they can be used within functions, or function arguments.
|
Variables, unlike constants, are mutable. They cannot be declared in a
|
||||||
|
*global scope*, but they can be declared and used within functions, or
|
||||||
|
as function parameters.
|
||||||
|
|
||||||
> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs.
|
> 💡 Do not worry if you do not understand the function syntax yet. We
|
||||||
|
> will get to it in upcoming sections of this documentation.
|
||||||
|
|
||||||
> ⚠️ Please be wary that mutation only works within the function scope itself, values outside of the function scope will not be affected.
|
> ⚠️ Please be wary that mutation only works within the function scope
|
||||||
|
> itself, values outside of the function scope will not be affected.
|
||||||
|
|
||||||
|
|
||||||
|
```pascaligo group=b
|
||||||
|
// The following is invalid: use `const` for global values instead.
|
||||||
|
// var four : int = 4
|
||||||
|
|
||||||
```pascaligo
|
function add (const a : int; const b : int) : int is
|
||||||
// won't work, use const for global values instead
|
block {
|
||||||
// var four: int = 4;
|
var c : int := a + b
|
||||||
|
} with c
|
||||||
function add(const a: int; const b: int) : int is
|
|
||||||
block {
|
|
||||||
var c : int := a + b;
|
|
||||||
} with c
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ⚠️ Notice the assignment operator `:=` for `var`, instead of `=` for
|
||||||
|
> constants.
|
||||||
|
|
||||||
> ⚠️ Notice the different assignment operator `:=`
|
You can run the `add` function defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
You can run the `add` function defined above using the LIGO compiler like this:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
ligo run-function -s pascaligo gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo add '(1,1)'
|
ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo add '(1,1)'
|
||||||
# Outputs: 2
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
As expected from a functional language, CameLIGO uses value-binding
|
As expected in the pure subset of a functional language, CameLIGO only
|
||||||
for variables rather than assignment. Variables are changed by replacement,
|
features constant values: once they are declared, the value cannot be
|
||||||
with a new value being bound in place of the old one.
|
changed (or "mutated").
|
||||||
|
|
||||||
> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs.
|
> 💡 Do not worry if you do not understand the function syntax yet. We
|
||||||
|
> will get to it in upcoming sections of this documentation.
|
||||||
|
|
||||||
```cameligo
|
```cameligo group=c
|
||||||
|
let add (a : int) (b : int) : int =
|
||||||
let add (a: int) (b: int) : int =
|
|
||||||
let c : int = a + b in c
|
let c : int = a + b in c
|
||||||
```
|
```
|
||||||
|
|
||||||
You can run the `add` function defined above using the LIGO compiler like this:
|
You can run the `add` function defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
```shell
|
```shell
|
||||||
ligo run-function -s cameligo gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo add '(1,1)'
|
ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo add '(1,1)'
|
||||||
# Outputs: 2
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
As expected from a functional language, ReasonLIGO uses value-binding
|
As expected in the pure subset of a functional language, ReasonLIGO
|
||||||
for variables rather than assignment. Variables are changed by replacement,
|
only features constant values: once they are declared, the value
|
||||||
with a new value being bound in place of the old one.
|
cannot be changed (or "mutated").
|
||||||
|
|
||||||
> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs.
|
> 💡 Do not worry if you do not understand the function syntax yet. We
|
||||||
|
> will get to it in upcoming sections of this documentation.
|
||||||
|
|
||||||
```reasonligo
|
```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;
|
|
||||||
c;
|
c;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
You can run the `add` function defined above using the LIGO compiler like this:
|
You can run the `add` function defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
```shell
|
```shell
|
||||||
ligo run-function -s reasonligo gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo add '(1,1)'
|
ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo add '(1,1)'
|
||||||
# Outputs: 2
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ ligo: : Lexical error in file "negative_byte_sequence.religo", line 1, character
|
|||||||
run_ligo_bad [ "compile-contract" ; "../../test/lexer/reserved_name.ligo" ; "main" ] ;
|
run_ligo_bad [ "compile-contract" ; "../../test/lexer/reserved_name.ligo" ; "main" ] ;
|
||||||
[%expect {|
|
[%expect {|
|
||||||
ligo: : Lexical error in file "reserved_name.ligo", line 1, characters 4-13:
|
ligo: : Lexical error in file "reserved_name.ligo", line 1, characters 4-13:
|
||||||
Reserved name: arguments.
|
Reserved name: "arguments".
|
||||||
Hint: Change the name.
|
Hint: Change the name.
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ ligo: : Lexical error in file "reserved_name.ligo", line 1, characters 4-13:
|
|||||||
run_ligo_bad [ "compile-contract" ; "../../test/lexer/reserved_name.religo" ; "main" ] ;
|
run_ligo_bad [ "compile-contract" ; "../../test/lexer/reserved_name.religo" ; "main" ] ;
|
||||||
[%expect {|
|
[%expect {|
|
||||||
ligo: : Lexical error in file "reserved_name.religo", line 1, characters 4-7:
|
ligo: : Lexical error in file "reserved_name.religo", line 1, characters 4-7:
|
||||||
Reserved name: end.
|
Reserved name: "end".
|
||||||
Hint: Change the name.
|
Hint: Change the name.
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ ligo: : Lexical error in file "reserved_name.religo", line 1, characters 4-7:
|
|||||||
run_ligo_bad [ "compile-contract" ; "../../test/lexer/reserved_name.mligo" ; "main" ] ;
|
run_ligo_bad [ "compile-contract" ; "../../test/lexer/reserved_name.mligo" ; "main" ] ;
|
||||||
[%expect {|
|
[%expect {|
|
||||||
ligo: : Lexical error in file "reserved_name.mligo", line 1, characters 4-10:
|
ligo: : Lexical error in file "reserved_name.mligo", line 1, characters 4-10:
|
||||||
Reserved name: object.
|
Reserved name: "object".
|
||||||
Hint: Change the name.
|
Hint: Change the name.
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user