ligo/gitlab-pages/docs/language-basics/functions.md

377 lines
10 KiB
Markdown
Raw Normal View History

2019-06-11 05:37:12 +04:00
---
id: functions
title: Functions
---
import Syntax from '@theme/Syntax';
2020-02-12 01:29:12 +04:00
LIGO functions are the basic building block of contracts. For example,
entrypoints are functions and each smart contract needs a main
function that dispatches control to the entrypoints (it is not already
the default entrypoint).
The semantics of function calls in LIGO is that of a *copy of the
arguments but also of the environment*. In the case of PascaLIGO, this
means that any mutation (assignment) on variables outside the scope of
the function will be lost when the function returns, just as the
mutations inside the functions will be.
2020-02-10 22:07:20 +04:00
## Declaring Functions
<Syntax syntax="pascaligo">
2020-02-10 22:07:20 +04:00
There are two ways in PascaLIGO to define functions: with or without a
*block*.
### Blocks
2019-06-11 05:37:12 +04:00
2020-02-05 19:28:40 +04:00
In PascaLIGO, *blocks* enable the sequential composition of
2020-02-06 14:47:41 +04:00
instructions into an isolated scope. Each block needs to include at
2020-02-10 22:07:20 +04:00
least one instruction.
2019-06-11 05:37:12 +04:00
2019-12-26 15:51:42 +04:00
```pascaligo skip
2020-01-28 18:13:50 +04:00
block { a := a + 1 }
2019-06-11 05:37:12 +04:00
```
2020-02-10 22:07:20 +04:00
If we need a placeholder, we use the instruction `skip` which leaves
the state unchanged. The rationale for `skip` instead of a truly
empty block is that it prevents you from writing an empty block by
mistake.
2020-02-05 19:28:40 +04:00
```pascaligo skip
2020-02-10 22:07:20 +04:00
block { skip }
2020-02-05 19:28:40 +04:00
```
2019-06-11 05:37:12 +04:00
2020-02-10 22:07:20 +04:00
Blocks are more versatile than simply containing instructions: they
can also include *declarations* of values, like so:
2019-06-11 05:37:12 +04:00
2020-02-10 22:07:20 +04:00
```pascaligo skip
block { const a : int = 1 }
```
2020-01-28 18:13:50 +04:00
Functions in PascaLIGO are defined using the `function` keyword
followed by their `name`, `parameters` and `return` type definitions.
2020-02-05 19:28:40 +04:00
Here is how you define a basic function that computes the sum of two
integers:
2019-12-26 15:51:42 +04:00
```pascaligo group=a
2020-02-05 19:28:40 +04:00
function add (const a : int; const b : int) : int is
block {
const sum : int = a + b
} with sum
```
The function body consists of two parts:
2020-02-10 22:07:20 +04:00
- `block { <instructions and declarations> }` is the logic of the function;
- `with <value>` is the value returned by the function.
2020-02-10 22:07:20 +04:00
### Blockless functions
Functions that can contain all of their logic into a single
*expression* can be defined without the need of a block:
```pascaligo
function identity (const n : int) : int is block { skip } with n // Bad! Empty block not needed!
function identity (const n : int) : int is n // Blockless
```
The value of the expression is implicitly returned by the
function. Another example is as follows:
2019-12-26 15:51:42 +04:00
```pascaligo group=b
2020-02-05 19:28:40 +04:00
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
```
2019-06-11 05:37:12 +04:00
</Syntax>
<Syntax syntax="cameligo">
2019-06-11 05:37:12 +04:00
2020-02-05 19:28:40 +04:00
Functions in CameLIGO are defined using the `let` keyword, like other
values. The difference is that a succession of parameters is provided
after the value name, followed by the return type. This follows OCaml
syntax. For example:
```cameligo group=c
let add (a : int) (b : int) : int = a + b
```
You can call the function `add` defined above using the LIGO compiler
like this:
```shell
ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.mligo add '(1,2)'
# Outputs: 3
```
2020-01-28 18:13:50 +04:00
CameLIGO is a little different from other syntaxes when it comes to
function parameters. In OCaml, functions can only take one
parameter. To get functions with multiple arguments like we are used
2020-02-05 19:28:40 +04:00
to in imperative programming languages, a technique called
2020-01-28 18:13:50 +04:00
[currying](https://en.wikipedia.org/wiki/Currying) is used. Currying
essentially translates a function with multiple arguments into a
series of single argument functions, each returning a new function
accepting the next argument until every parameter is filled. This is
2020-02-05 19:28:40 +04:00
useful because it means that CameLIGO supports
2020-01-28 18:13:50 +04:00
[partial application](https://en.wikipedia.org/wiki/Partial_application).
Currying is however *not* the preferred way to pass function arguments
in CameLIGO. While this approach is faithful to the original OCaml,
2020-02-05 19:28:40 +04:00
it is costlier in Michelson than naive function execution accepting
multiple arguments. Instead, for most functions with more than one
parameter, we should gather the arguments in a
2020-01-28 18:13:50 +04:00
[tuple](language-basics/sets-lists-tuples.md) and pass the tuple in as
a single parameter.
2020-02-12 01:29:12 +04:00
Here is how you define a basic function that accepts two integers and
returns an integer as well:
2019-12-26 15:51:42 +04:00
```cameligo group=b
2020-02-05 19:28:40 +04:00
let add (a, b : int * int) : int = a + b // Uncurried
let add_curry (a : int) (b : int) : int = add (a, b) // Curried
let increment : int -> int = add_curry 1 // Partial application
2020-02-05 19:28:40 +04:00
```
2020-02-05 19:28:40 +04:00
You can run the `increment` function defined above using the LIGO
compiler like this:
```shell
ligo run-function gitlab-pages/docs/language-basics/src/functions/curry.mligo increment 5
# Outputs: 6
2019-06-11 05:37:12 +04:00
```
2020-02-05 19:28:40 +04:00
The function body is a single expression, whose value is returned.
2019-12-11 13:34:08 +04:00
</Syntax>
<Syntax syntax="reasonligo">
2020-02-12 01:29:12 +04:00
Functions in ReasonLIGO are defined using the `let` keyword, like
other values. The difference is that a tuple of parameters is provided
after the value name, with its type, then followed by the return type.
2019-12-11 13:34:08 +04:00
2020-02-05 19:28:40 +04:00
Here is how you define a basic function that sums two integers:
2019-12-26 15:51:42 +04:00
```reasonligo group=b
2020-02-05 19:28:40 +04:00
let add = ((a, b): (int, int)) : int => a + b;
2019-12-11 13:34:08 +04:00
```
2020-02-05 19:28:40 +04:00
You can call the function `add` defined above using the LIGO compiler
like this:
```shell
ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.religo add '(1,2)'
# Outputs: 3
```
2019-12-11 13:34:08 +04:00
2020-02-12 01:29:12 +04:00
As in CameLIGO and with blockless functions in PascaLIGO, the function
body is a single expression, whose value is returned.
If the body contains more than a single expression, you use block
between braces:
```reasonligo group=b
let myFun = ((x, y) : (int, int)) : int => {
let doubleX = x + x;
let doubleY = y + y;
doubleX + doubleY
};
```
</Syntax>
2020-02-05 19:28:40 +04:00
## Anonymous functions (a.k.a. lambdas)
It is possible to define functions without assigning them a name. They
are useful when you want to pass them as arguments, or assign them to
a key in a record or a map.
2020-02-05 19:28:40 +04:00
Here is how to define an anonymous function:
<Syntax syntax="pascaligo">
2019-12-26 15:51:42 +04:00
```pascaligo group=c
2020-02-05 19:28:40 +04:00
function increment (const b : int) : int is
(function (const a : int) : int is a + 1) (b)
const a : int = increment (1); // a = 2
```
You can check the value of `a` defined above using the LIGO compiler
like this:
```shell
ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.ligo a
# Outputs: 2
```
</Syntax>
<Syntax syntax="cameligo">
2019-12-26 15:51:42 +04:00
```cameligo group=c
2020-02-05 19:28:40 +04:00
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
```
</Syntax>
<Syntax syntax="reasonligo">
2019-12-26 15:51:42 +04:00
```reasonligo group=c
2020-02-12 01:29:12 +04:00
let increment = (b : int) : int => ((a : int) : int => a + 1) (b);
2020-02-10 22:07:20 +04:00
let a : int = increment (1); // a == 2
2020-02-05 19:28:40 +04:00
```
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
2019-12-10 17:47:31 +04:00
```
</Syntax>
2020-02-06 15:32:23 +04:00
If the example above seems contrived, here is a more common design
pattern for lambdas: to be used as parameters to functions. Consider
the use case of having a list of integers and mapping the increment
function to all its elements.
<Syntax syntax="pascaligo">
2020-02-06 15:32:23 +04:00
```pascaligo group=c
function incr_map (const l : list (int)) : list (int) is
List.map (function (const i : int) : int is i + 1, l)
2020-02-06 15:32:23 +04:00
```
> Note that `list_map` is *deprecated*.
You can call the function `incr_map` defined above using the LIGO
compiler like so:
2020-02-06 15:32:23 +04:00
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/functions/incr_map.ligo incr_map
"list [1;2;3]"
# Outputs: [ 2 ; 3 ; 4 ]
```
</Syntax>
<Syntax syntax="cameligo">
2020-02-06 15:32:23 +04:00
```cameligo group=c
let incr_map (l : int list) : int list =
List.map (fun (i : int) -> i + 1) l
```
You can call the function `incr_map` defined above using the LIGO compiler
like so:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/functions/incr_map.mligo incr_map
"list [1;2;3]"
# Outputs: [ 2 ; 3 ; 4 ]
```
</Syntax>
<Syntax syntax="reasonligo">
2020-02-06 15:32:23 +04:00
```reasonligo group=c
let incr_map = (l : list (int)) : list (int) =>
List.map ((i : int) => i + 1, l);
```
You can call the function `incr_map` defined above using the LIGO compiler
like so:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/functions/incr_map.religo incr_map
"list [1;2;3]"
# Outputs: [ 2 ; 3 ; 4 ]
```
</Syntax>
2020-03-18 12:49:35 +04:00
## Nested functions (also known as closures)
It's possible to place functions inside other functions. These functions
have access to variables in the same scope.
<Syntax syntax="pascaligo">
```pascaligo
function closure_example (const i : int) : int is
block {
function closure (const j : int) : int is i + j
} with closure (i)
```
</Syntax>
<Syntax syntax="cameligo">
```cameligo
let closure_example (i : int) : int =
let closure : int -> int = fun (j : int) -> i + j in
closure i
```
</Syntax>
<Syntax syntax="reasonligo">
```reasonligo
let closure_example = (i : int) : int => {
let closure = (j: int): int => i + j;
closure(i);
};
```
</Syntax>
2020-03-10 17:24:57 +04:00
## Recursive function
2020-03-11 14:18:39 +04:00
LIGO functions are not recursive by default, the user need to indicate that the function is recursive.
2020-03-10 17:24:57 +04:00
At the moment, recursive function are limited to one (possibly tupled) parameter and recursion is
limited to tail recursion (i.e the recursive call should be the last expression of the function)
<Syntax syntax="pascaligo">
2020-03-11 20:04:49 +04:00
In PascaLigo recursive functions are defined using the `recursive` keyword
2020-03-10 17:24:57 +04:00
```pascaligo group=d
recursive function sum (const n : int; const acc: int) : int is
if n<1 then acc else sum(n-1,acc+n)
recursive function fibo (const n: int; const n_1: int; const n_0 :int) : int is
if n<2 then n_1 else fibo(n-1,n_1+n_0,n_1)
```
</Syntax>
<Syntax syntax="cameligo">
2020-03-11 20:04:49 +04:00
In CameLigo recursive functions are defined using the `rec` keyword
2020-03-10 17:24:57 +04:00
```cameligo group=d
let rec sum ((n,acc):int * int) : int =
if (n < 1) then acc else sum (n-1, acc+n)
let rec fibo ((n,n_1,n_0):int*int*int) : int =
if (n < 2) then n_1 else fibo (n-1, n_1 + n_0, n_1)
```
</Syntax>
<Syntax syntax="reasonligo">
2020-03-11 20:04:49 +04:00
In ReasonLigo recursive functions are defined using the `rec` keyword
2020-03-10 17:24:57 +04:00
```reasonligo group=d
let rec sum = ((n, acc) : (int,int)): int =>
if (n < 1) {acc;} else {sum ((n-1,acc+n));};
let rec fibo = ((n, n_1, n_0) : (int,int,int)): int =>
if (n < 2) {n_1;} else {fibo ((n-1,n_1+n_0,n_1));};
```
</Syntax>