Functions
LIGO functions are the basic building blocks of contracts. Contracts can have any number of functions.
Entrypoints are a special case of functions. Outside consumers can call only the functions that are designated as entrypoints. Entrypoints must also follow certain rules that do not apply to functions in general. See Entrypoints.
As in JavaScript, you can declare functions with the function
keyword or the const
or let
keywords.
Here are some examples:
This type of function must use the return
keyword to set the return value.
If it does not use return
, the function returns unit by default.
Also as in JavaScript, when you declare functions with the const
or let
keywords, you can replace the body of the function with a single expression and omit the return
keyword.
The add_expression
function in the following example is equivalent to the add_function
and add_function_const
functions in the previous example:
To call a function, use its name and pass the correct number and type of parameters, as in this example:
By default, LIGO warns about unused parameters inside functions. If the function does not use one of its parameters, you can prefix its name with an underscore to hide the warning, as in this example:
Polymorphic functions
Polymorphic functions can take advantage of parametric types to accept a wide variety of inputs instead of only a single input type. For more information, see Polymorphic functions.
Scope and side effects
Functions can access, or capture, immutable variables and functions that are outside their code as long as those variables and functions are in their scope. For example, a function can capture another function in its scope so it can call it, as in this example:
Capturing also works with nested functions just like two functions at the same scope level:
Functions cannot change values outside their scope, including values passed as parameters. In this example, a function tries to change a variable and a parameter but fails:
Currying functions
To simplify a function for use in different contexts, you can create new functions that decompose the original function into simpler functions that take fewer parameters. Creating these simplified functions is called currying, and it enables partial application.
For example, this code creates a function that accepts a single parameter, a tuple that contains two integers. Then it creates a curried function that accepts two separate integers as parameters and passes them to the first function as a tuple. Then it uses partial application to create a function that accepts one parameter and passes it to the curried function:
Function expressions
Sometimes you need to use a function only once, so it doesn't need a name. Also, giving the function a name might incur a slight risk of cluttering the scope or being captured unintentionally. Functions without names are called anonymous functions, function expressions, or lambdas.
Function expressions are called arrow functions in JsLIGO:
Note that when there is a single parameter that is not given a type, the parentheses are not necessary, but they are if the return type is given, as in this example:
The anonymous function (x,y) => x + y
is an expression, and you can use it without a name in contexts where functions of type (x: int, y: int) => int
are valid.
Note: When a function takes one argument that is a tuple, parentheses are mandatory, as in this example:
That function is different from the function (x,y) => x + y
, which takes two arguments.
In other words, sum
has type (x: int, y: int) => int
and comp_sum
has type ([x,y] : [int,int]) => int
.
Recursion
Recursive functions are defined and called using the same syntax as non-recursive functions.
This means that all values, including functions, declared in the same block or top-level scope, must have different names, because they can all potentially be mutually recursive.
Higher-order functions
Functions that take a function as a parameter or return a function are known as higher-order functions. This example accepts two functions and composes them into one, as in mathematics:
You can also pass named functions as arguments:
Inlining
Inlining is the process of embedding the code of a function instead of storing the function as a separate block of code. To save space, the LIGO compiler inlines function code rather than using a separate function when the function is used only once and is pure, which means it has no side effects. In this context, side effects include creating operations or exceptions.
The compiler does not inline functions that are used more than once or are not pure because doing so often results in larger contracts. However, in some cases, you may want to force the compiler to inline function code. You can force the compiler to inline functions that are used more than once, but you cannot force the compiler to inline functions that cause side effects.
To force inlining, use the @inline
decorator.
You can measure the difference between inlining and not inlining with the info measure-contract
command.
In this case, inlining the function saves space because the function is so small.
This table shows the results of the command ligo info measure-contract inline.jsligo
with and without the @inline
decorator:
With inlining | 46 bytes |
Without inlining | 97 bytes |
Note that these results can change due to ongoing work to optimise output of the LIGO compiler.