TONTONDocs
Type system

Structures

Tolk supports structures.

struct Point {
    x: int
    y: int
}

fun calcMaxCoord(p: Point) {
    return p.x > p.y ? p.x : p.y
}

fun demo() {
    // declared using object-literal syntax
    var p: Point = { x: 10, y: 20 };
    calcMaxCoord(p);

    // constructed using object-literal syntax
    calcMaxCoord({ x: 10, y: 20 });
}

Distinct structure types

Structures with identical fields are not assignable to each other:

struct SomeA { v: int }
struct SomeB { v: int }

fun acceptA(a: SomeA) {}

fun demo(a: SomeA, b: SomeB) {
    b = a;      // error, can not assign `SomeA` to `SomeB`
    acceptA(b); // error, can not pass `SomeB` to `SomeA`
}

Even though SomeA and SomeB have identical layouts, they represent distinct types.

Contextual type inference

The compiler infers types from context. In the example below, the compiler determines that { ... } has type StoredInfo based on the parameter type:

fun store(info: StoredInfo) {
    // ...
}

fun demo() {
    store({
        counterValue: ...,
        ownerAddress: ...,
    });
}

The same applies to return values and assignments:

fun loadData(): StoredInfo {
    return {
        counterValue: ...,
        ownerAddress: ...,
    }
}

fun demo() {
    var s: StoredInfo = { counterValue, ... };
    var s: (int, StoredInfo) = (0, { counterValue, ... });
}

Explicit type hints

Explicit type hints are available. In addition to the plain { ... } syntax, the form StructName { ... } can be used. The snippet below is equivalent to the previous example:

fun loadData() {
    return StoredInfo {
        counterValue: ...,
        ownerAddress: ...,
    }
}

fun demo() {
    var s = StoredInfo { counterValue, ... };
    var s = (0, StoredInfo { counterValue, ... });
}

When neither contextual information nor an explicit type hint is available, the type cannot be inferred and an error is produced.

val o = { x: 10, y: 20 };    // error, what type is it?

Methods

Methods are declared as extension functions:

fun Point.calcSquare(self) {
    return self.x * self.y
}

Without self parameter, a method will be static. By default, self is immutable. The form mutate self enables mutation.

Serialization prefixes

Serialization prefixes do not affect typing or layout. The syntax struct (PREFIX) Name { ... } specifies a serialization prefix. The prefix affects only the binary representation; all other aspects remain unchanged.

struct (0x12345678) CounterIncrement {
    byValue: uint32
}

fun demo(inc: CounterIncrement) {
    // `inc` has one field; the prefix is not a property
    inc.byValue
    // `inc` is still one TVM `INT` on the stack
}

Structure syntax

Structure syntax includes:

  • shorthand field syntax { x, y };
  • default field values (a: int32 = 10);
  • private and readonly fields;
  • serialization prefixes (opcodes).

Stack layout and serialization

Fields are placed on the stack sequentially and are serialized in the same order. If a struct has a prefix, it is written first.

Serialization behavior can be overridden by defining custom serializers.

fun MyStruct.packToBuilder(self, mutate b: builder) {
    // custom cell-composition logic
}

fun MyStruct.unpackFromSlice(mutate s: slice) {
    // custom cell-parsing logic
}

Last updated on

On this page