Function declaration
A function is declared using thefun keyword, followed by the function name and parameters:
Parameter type
Each function parameter must have an explicit type.Return type
A function may declare its return type explicitly:return statements:
return statements produce different types, the compiler reports an error. Union types are not inferred, as this usually indicates a bug.
Default parameter value
Default values are placed after the type and must be constant expressions:Method declaration
Methods are declared as extension functions with a receiver specified before the name:Instance and static methods
If the first parameter isself, it is an instance method: fun <receiver>.f(self, ...). Otherwise, it is a static method: fun <receiver>.f(...).
obj.method(). Static methods are invoked on the receiver type.
Receiver and self
The type of self is determined by the receiver. All parameters after self must have explicit types. If the return type is omitted, it is inferred.
Methods for non-struct types
Methods are not limited to structures. They may be declared for any receiver type, unions, aliases, and primitives:fun cell.hash(self).
Immutability of self
In instance methods, self is immutable by default.
To allow modifications, declare mutate self explicitly.
Returning self and method chaining
Returning self makes a method chainable. To enable chaining, declare the return type as self.
For example, all methods of builder return self, which allows consecutive calls like b.storeXXX().storeXXX().
Generic functions
A function declared with type parametersT is called a generic function.
Type parameter inference
When calling a generic function, the compiler automatically infers type arguments:Explicit type arguments
Type arguments may be specified explicitly usingf<...>(args):
Multiple type parameters
A generic function may declare multiple type parameters:Default type parameters
Generic functions may define default type parameters:Generic functions as values
Since Tolk supports first-class functions, generic functions can be passed as arguments and stored in variables.Type inference limitations
Although type parameters are usually inferred from function arguments, there are cases where a type parameter<T> cannot be inferred because it does not depend on them. Tuples are a common example:
Assigning generic functions to variables
A generic function may be assigned to a variable. Since this is not a function call,<T> must be specified explicitly:
Generic methods
Declaring a method for a generic type does not differ from declaring any other method. When parsing the receiver, the compiler treats unknown symbols as type parameters.Instance generic methods
Generic methods can be instance methods by declaringself:
Generic methods as values
Generic methods can be also used as first-class functions:Methods for generic structure
Methods for generic structures can themselves be generic:Specialization and overloading
Overloading and partial specialization allow declaring multiple methods with the same name for different receiver types without conflicts.Methods for any receiver
A method may be declared for an arbitrary receiver by using an unknown symbol, typicallyT, as the receiver types. Such a method is applicable to any type.
Overloading by receiver type
Specific receiver types do not conflict:Partial specialization for generic receivers
A method declared for a generic receiver may have specialized implementations for predefined types or patterns. Consider an iterator over a tuple of slices, where each slice encodes a value of typeT:
TupleIterator<int32> or TupleIterator<Point>, next() decodes the next slice and returns a value of type T. However, additional requirements are:
TupleIterator<slice>should returndata[i]without callingfromSlice();TupleIterator<Cell<T>>should unpack a cell and returnT, notCell<T>.
map<K, V>, with specialized implementations for specific receiver patterns:
- when
V = K, - and when
Vis another map, the method behavior differs.
If a user declares
struct T, methods like fun T.copy() are interpreted as specializations rather than as generic methods.
To avoid ambiguity, do not use single-letter type names for concrete types; prefer descriptive names.Auto-inline small functions
The compiler automatically inlines small functions in-place when possible. For example:Attributes
A function or method may be preceded by one or several attributes:@inlineforces a function to be inlined in-place. Typically unnecessary, as the compiler performs inlining automatically.@inline_refenables a special form of inlining where the function body is embedded as a child cell reference in the resulting bytecode.@noinlineturns off inlining for a function, for example, for a slow path.@method_id(<number>)is a low-level annotation to manually override the TVMmethod_id.@pureindicates that a function does not modify global state, including TVM state. If its result is unused, the call may be removed and typically used in assembler functions.@deprecatedmarks a function as deprecated; it exists only for backward compatibility.@custom(<anything>)is a custom expression that is not analyzed by the compiler.