Functions
FunC program is essentially a list of function declarations/definitions and global variable declarations. This section covers the first topic.
Any function declaration or definition starts with a common pattern and one of the three things goes next:
-
single
;
, which means that the function is declared but not defined yet. It may be defined later in the same file or in some other file which is passed before the current one to the FunC compiler. For example,int add(int x, int y);
is a simple declaration of a function named
add
of type(int, int) -> int
. -
assembler function body definition. It is the way to define functions by low-level TVM primitives for later use in FunC program. For example,
int add(int x, int y) asm "ADD";
is an assembler definition of the same function
add
of type(int, int) -> int
which will translate to the TVM opcodeADD
. -
ordinary block statement function body definition. It is the usual way to define functions. For example,
int add(int x, int y) {
return x + y;
}is an ordinary definition of the
add
function.
Function declaration
As said before, any function declaration or definition starts with a common pattern. The following is the pattern:
[<forall declarator>] <return_type> <function_name>(<comma_separated_function_args>) <specifiers>
where [ ... ]
corresponds to an optional entry.
Function name
Function name can be any identifier and also it can start with .
or ~
symbols. The meaning of those symbols is explained in the statements section.
For example, udict_add_builder?
, dict_set
and ~dict_set
are valid and different function names. (They are defined in stdlib.fc.)
Special function names
FunC (actually Fift assembler) has several reserved function names with predefined ids.
main
andrecv_internal
have id = 0recv_external
has id = -1run_ticktock
has id = -2
Every program must have a function with id 0, that is, main
or recv_internal
function.
run_ticktock
is called in ticktock transactions of special smart contracts.
Receive internal
recv_internal
is called when a smart contract receives an inbound internal message.
There are some variables at the stack when TVM initiates, by setting arguments in recv_internal
we give smart-contract code awareness about some of them. Those arguments about which code will not know, will just lie at the bottom of the stack never touched.
So each of the following recv_internal
declarations is correct, but those with less variables will spend slightly less gas (each unused argument adds additional DROP
instructions)
() recv_internal(int balance, int msg_value, cell in_msg_cell, slice in_msg) {}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) {}
() recv_internal(cell in_msg_cell, slice in_msg) {}
() recv_internal(slice in_msg) {}
Receive external
recv_external
is for inbound external messages.
Return type
Return type can be any atomic or composite type as described in the types section. For example,
int foo();
(int, int) foo'();
[int, int] foo''();
(int -> int) foo'''();
() foo''''();
are valid function declarations.
Type inference is also allowed. For example,
_ pyth(int m, int n) {
return (m * m - n * n, 2 * m * n, m * m + n * n);
}
is a valid definition of function pyth
of type (int, int) -> (int, int, int)
, which computes Pythagorean triples.
Function arguments
Function arguments are separated by commas. The valid declarations of an argument are following:
- Ordinary declaration: type + name. For example,
int x
is a declaration of argument of typeint
and namex
in the function declaration() foo(int x);
- Unused argument declaration: only type. For example,
is a valid function definition of type
int first(int x, int) {
return x;
}(int, int) -> int
- Argument with an inferred type declaration: only name.
For example,
is a valid function definition of type
int inc(x) {
return x + 1;
}int -> int
. Theint
type ofx
is inferred by the type-checker.
Note that although a function may look like a function of several arguments, it's actually a function of one tensor-type argument. To see the difference, please refer to function application. Nevertheless, the components of the argument tensor are conventionally called function arguments.
Function calls
Non-modifying methods
Non-modifying function supports short function call form with .
example(a);
a.example();
If a function has at least one argument, it can be called as a non-modifying method. For example, store_uint
has type (builder, int, int) -> builder
(the second argument is the value to store, and the third is the bit length). begin_cell
is a function that creates a new builder. The following codes are equivalent:
builder b = begin_cell();
b = store_uint(b, 239, 8);
builder b = begin_cell();
b = b.store_uint(239, 8);