Skip to main content
Tolk allows declaring functions and methods:
  • fun f(<params>) — a function
  • fun <receiver>.f(<params>) — a method:
    • fun <receiver>.f(self, ...) — an instance method
    • fun <receiver>.f(...) — a static method (no self)
All features are identical except for the receiver. Most examples below use functions, but all concepts equally apply to methods.

Types for parameters are required

The return type is auto-inferred if omitted

Either specify return type manually:
or it is inferred from the return statements, similar to TypeScript:
If various returns have different types, the compiler will fire an error instead of inferring a union type, because this typically indicates a bug.

Default values for parameters

Default values are placed after the type, allowing constant expressions:

Methods: for any type, including structures

A method is declared as an extension function, similar to Kotlin. If the first parameter is self, it’s an instance method. Otherwise, it’s a static method.
Instance methods are invoked via obj.method(), whereas static methods — on a type:
Methods are not limited to structures. They may exist for any receiver, including unions, aliases, and primitives:
All standard methods are declared this way: fun cell.hash(self), etc. The type of self is determined by the receiver. All parameters after should have their types set. The return type is inferred if omitted.

self is immutable by default

To allow modifications, declare mutate self explicitly. For details, see mutability.

return self makes a chainable method

To make a method chainable, declare it as fun (...): self. For instance, all methods for builder return self, which allows b.storeXXX().storeXXX():

Small functions are automatically inlined

Create one-liner methods without any worries. The compiler automatically inlines them in-place, targeting zero overhead. For example, this program
is reduced to “return 2” in assembler:
For details, see compiler optimizations.

@attributes

A function or method may be preceded by one or several attributes:
The following attributes are allowed:
  • @inline — Forces a function to be inlined in-place. Typically unnecessary, as the compiler performs inlining automatically.
  • @inline_ref — Turns on a special version of inlining: its body will be embedded as a child cell reference in the resulting bytecode.
  • @noinline — Disables inlining for a function. For example, if it’s a “slow path”.
  • @method_id(<number>) — Low-level annotation to manually override TVM method_id.
  • @pure — Indicates that a function does not modify global state (including TVM one). If its result is unused, a call could be deleted. Typically used in assembler functions.
  • @deprecated — A function is not recommended for use and exists only for backwards compatibility.
  • @custom(<anything>) — A custom expression, not analyzed by the compiler.

Assembler functions

Tolk supports declaring low-level assembler functions with embedded Fift code:
It’s a low-level feature and requires deep knowledge of Fift and TVM. See assembler functions.

Anonymous functions (lambdas)

Tolk supports first-class functions: they can be passed as callbacks. Both named functions and function expressions may be referenced:
See callables.

Generic functions

A function declared as fun f<T>(...) is a generic one. T is called a “type parameter”.
When calling a generic function, the compiler automatically infers type arguments:
Type arguments may also be specified explicitly using f<...>(args):
Functions may declare multiple type parameters:
Since Tolk supports first-class functions, various custom invokers for general-purpose frameworks can be expressed:
Default type parameters are supported, like fun f<T1, T2 = int>, but they cannot reference one another currently. Although type parameters are usually inferred from the arguments, there are edge cases where T cannot be inferred because it does not depend on them. For example, tuples. A tuple may have anything inside it, that’s why invoking tuple.get lacks without T:
A generic function may be assigned to a variable, but T must be specified explicitly because this is not a call:

Generic methods

Declaring a method for a generic type does not differ from declaring any other method. The compiler treats unknown symbols as type parameters while parsing the receiver:
Generic methods can be also used as first-class functions:
Methods for generic structures can themselves be generic:

Methods for “any receiver”

Any unknown symbol (typically T) may be used to define a method applicable to any type:
Note that “any receivers” do not conflict with specific ones.
This is known as “overloading” or “partial specialization”.

Partial specialization of generic methods

Specializing generic methods allows some logic to work differently for predefined types or patterns. Consider the following example. Suppose, there is an iterator over a tuple of slices, each slice contains encoded T.
Thus, given TupleIterator<int32> or TupleIterator<Point>, next() will decode the next slice and return it. But additional requirements are:
  • TupleIterator<slice> should just return data[i] without calling fromSlice()
  • TupleIterator<Cell<T>> should return not Cell<T> but unpack a cell and return T
Tolk allows overloading methods for more specific type patterns:
Another example. Declare an extension method for map<K, V>, but
  • if V = K, provide a different implementation
  • if V is another map, modify the behavior