08.04.202509:12
🫧 Tolk v0.11: type aliases, union types, and pattern matching
This update might confuse you at first. You may wonder: "Why do we need this? How will it be useful?". But in the end, you'll see how everything comes together bringing seamless and generalized message handling.
✅ Notable changes in Tolk v0.11:
1. Type aliases
2. Union types
3. Pattern matching for types
4. Operators
5. Pattern matching for expressions
6. Semicolon for the last statement in a block can be omitted
PR on GitHub with detailed info.
✔ Type aliases
Tolk now supports type aliases, similar to TypeScript and Rust.
An alias creates a new name for an existing type but remains interchangeable with it. No performance overhead — a compile-time feature.
✔ Union types `T1 | T2 | ...`
They now allow a variable to hold multiple possible types.
Nullable types
At the TVM level, union types work as tagged unions — similar to Rust enums but more flexible.
✔ Pattern matching
The only way to work with union types is matching them:
Matching is based on smart casts — inside each branch, the variable is automatically narrowed to the matched type.
It can also be used as an expression:
So, `match` + smart casts are our way for union types. You may notice that it's close to enums in Rust. But we don't have enum. Union types are more general and powerful.
✔ `match` for expressions
As you see, match also works for constant expressions, similar to switch in other languages.
✔ Union types and TL/B `Either`
Look how clean this is:
No need to manually handle bits from the slice — it's naturally expressed in the type system!
✔ Union types and TL/B constructors
Moreover — handling incoming messages is beautifully expressed with union types.
✔ Union types and future structures
The ultimate goal? You'll describe each incoming message as a struct, create a union type for them, parse a slice, and just match over variants:
🌳 So, union types (that perfectly work with tensors) will seamlessly work with structures. With union types, you will declare both Either and different kinds of messages. Combined with intN and other types, they will allow to express (almost) any practical TL/B construction. They are not limited to message routing — in other words, message routing does not differ from handling any field, any storage, or any cell in general.
This update might confuse you at first. You may wonder: "Why do we need this? How will it be useful?". But in the end, you'll see how everything comes together bringing seamless and generalized message handling.
✅ Notable changes in Tolk v0.11:
1. Type aliases
type NewName =
2. Union types
T1 | T2 | ...
3. Pattern matching for types
4. Operators
is
and !is
5. Pattern matching for expressions
6. Semicolon for the last statement in a block can be omitted
PR on GitHub with detailed info.
✔ Type aliases
Tolk now supports type aliases, similar to TypeScript and Rust.
An alias creates a new name for an existing type but remains interchangeable with it. No performance overhead — a compile-time feature.
✔ Union types `T1 | T2 | ...`
They now allow a variable to hold multiple possible types.
Nullable types
T?
are now formally T | null
. At the TVM level, union types work as tagged unions — similar to Rust enums but more flexible.
✔ Pattern matching
The only way to work with union types is matching them:
Matching is based on smart casts — inside each branch, the variable is automatically narrowed to the matched type.
It can also be used as an expression:
So, `match` + smart casts are our way for union types. You may notice that it's close to enums in Rust. But we don't have enum. Union types are more general and powerful.
✔ `match` for expressions
As you see, match also works for constant expressions, similar to switch in other languages.
✔ Union types and TL/B `Either`
T1 | T2
will be directly mapped to TL-B (Either T1 T2)
. Look how clean this is:
(Either SmallPayload LargePayload)
becomes
No need to manually handle bits from the slice — it's naturally expressed in the type system!
✔ Union types and TL/B constructors
T1 | T2 | ...
is a typed way to describe multiple constructors from TL/B. Generally, they can be used anywhere inside a storage or a message. Moreover — handling incoming messages is beautifully expressed with union types.
✔ Union types and future structures
The ultimate goal? You'll describe each incoming message as a struct, create a union type for them, parse a slice, and just match over variants:
🌳 So, union types (that perfectly work with tensors) will seamlessly work with structures. With union types, you will declare both Either and different kinds of messages. Combined with intN and other types, they will allow to express (almost) any practical TL/B construction. They are not limited to message routing — in other words, message routing does not differ from handling any field, any storage, or any cell in general.
29.01.202515:28
Two months of absence: what was going on?
Two months have passed since the announcement of Tolk. You might be wondered, what was going on and why there we no releases yet.
Throughout all November, I've been working on the vision of the future. My goal was to "visualize" what Tolk v1.0 should look like. What's the language we're all targeting to, so that it solves lots of practical problems, avoids manual cells/slices manipulation, provides sufficient mechanisms for ABI generation, but still being zero overhead. I have created a giant roadmap (40 PDF pages!) describing the vision, and how, step by step, we're going to reach it.
Throughout all December, I've been constantly working on the compiler's kernel. As you know, Tolk is a fork of FunC. FunC compiler internals are very challenging to be extended and modified. The way FunC looks like is just a mirror of its internal implementation. Heading towards the future, I had to partially "untangle" this "legacy FunC core", so that in the future, it will be able to "interbreed" with features it was not originally designed for.
Currently I am done with this preparation. Tolk v0.7 has just been released. It contains a fully rewritten semantic analysis kernel (though almost invisible to the end user, huh).
✅ Notable changes in Tolk v0.7:
1. Under the hood: refactor and revamp compiler internals. AST-level semantic analysis kernel
2. Under the hood: rewrite the type system from Hindley-Milner to static typing
3. Clear and readable error messages on type mismatch
4. Generic functions
5. The
6. Type casting via
PR on GitHub with detailed info. IDE plugins are updated accordingly.
BTW, a new version of blueprint was also released. You can now update compilers (Tolk / FunC / Tact) independently, they became peer dependencies.
P.S. I'll uncover the details about planned Tolk v1.0 quite soon.
Two months have passed since the announcement of Tolk. You might be wondered, what was going on and why there we no releases yet.
Throughout all November, I've been working on the vision of the future. My goal was to "visualize" what Tolk v1.0 should look like. What's the language we're all targeting to, so that it solves lots of practical problems, avoids manual cells/slices manipulation, provides sufficient mechanisms for ABI generation, but still being zero overhead. I have created a giant roadmap (40 PDF pages!) describing the vision, and how, step by step, we're going to reach it.
Throughout all December, I've been constantly working on the compiler's kernel. As you know, Tolk is a fork of FunC. FunC compiler internals are very challenging to be extended and modified. The way FunC looks like is just a mirror of its internal implementation. Heading towards the future, I had to partially "untangle" this "legacy FunC core", so that in the future, it will be able to "interbreed" with features it was not originally designed for.
Currently I am done with this preparation. Tolk v0.7 has just been released. It contains a fully rewritten semantic analysis kernel (though almost invisible to the end user, huh).
✅ Notable changes in Tolk v0.7:
1. Under the hood: refactor and revamp compiler internals. AST-level semantic analysis kernel
2. Under the hood: rewrite the type system from Hindley-Milner to static typing
3. Clear and readable error messages on type mismatch
4. Generic functions
fun f(...)
and instantiations like f(...)
5. The
bool
type6. Type casting via
value as T
PR on GitHub with detailed info. IDE plugins are updated accordingly.
BTW, a new version of blueprint was also released. You can now update compilers (Tolk / FunC / Tact) independently, they became peer dependencies.
P.S. I'll uncover the details about planned Tolk v1.0 quite soon.
20.03.202509:12
🫧 Tolk v0.10: preparing for serialization — intN, bytesN, coins
This update lays the foundation of future auto-packing to/from cells by solving one critical question:
How should fields be serialized?
✅ Notable changes in Tolk v0.10:
1. Fixed-size integer types:
2. Type
3. Types
4. Replace
5. Trailing comma support
PR on GitHub with detailed info.
❓ Fixed-size integers? In TVM? What?
Imagine Tolk already has structures, and we define an incoming message:
A client sends this message following the TL/B schema:
But how do we tell the compiler that counter_id is int32 and inc_by is int64? This information is missing in the struct definition.
✖ Rejected approaches: why they fail
Several syntax ideas were considered:
Each of these quickly breaks down when handling more complex cases.
For example, how would we handle TL-B
And what about TL/B
With every new case, the syntax becomes more complex, ambiguous, and error-prone.
✔ The solution: `int32` as a first-class type
No annotations. No confusing
This scales perfectly:
These are distinct types. A variable can be
This makes serialization predictable, structured, and error-free.
✔ What about overflow?
A reasonable question: what happens if a value exceeds the limit?
Answer: no runtime overflow or clamping! It's just int at TVM.
* arithmetic works normally – v becomes 256
* no extra gas cost – no runtime bounds checks
* overflow will only happen at serialization
✔ Why is this the best approach?
Think of smart contracts as a black box:
- inputs are encoded (int32, uint64, etc.)
- inside the contract, arithmetic uses full 257-bit precision
- outputs are serialized again — overflow happens only at this stage
This is similar to how mulDivFloor(x,y,z) uses 513-bit precision internally. Your contract keeps precision internally and only enforces constraints at the border with an outside world.
🌳 Tolk will follow a type-based philosophy
This post covered the foundation of automatic serialization. The right way is to have a rich type system. Having nested types, having generics, having aliases — will allow to describe every practical TL/B case, but at a language level.
In v0.10, we introduce
How will
Stay tuned for the next update...
This update lays the foundation of future auto-packing to/from cells by solving one critical question:
How should fields be serialized?
✅ Notable changes in Tolk v0.10:
1. Fixed-size integer types:
int32
, uint64
, etc.2. Type
coins
and function ton("0.05")
3. Types
bytesN
and bitsN
(backed by slices at TVM)4. Replace
"..."c
postfixes with stringCrc32("...")
functions5. Trailing comma support
PR on GitHub with detailed info.
❓ Fixed-size integers? In TVM? What?
Imagine Tolk already has structures, and we define an incoming message:
A client sends this message following the TL/B schema:
counterIncrement
counter_id:int32
inc_by:int64
= CounterIncrement;
But how do we tell the compiler that counter_id is int32 and inc_by is int64? This information is missing in the struct definition.
✖ Rejected approaches: why they fail
Several syntax ideas were considered:
Each of these quickly breaks down when handling more complex cases.
For example, how would we handle TL-B
Maybe int32
? Would we write:
And what about TL/B
Both (Maybe int32) int64
?
With every new case, the syntax becomes more complex, ambiguous, and error-prone.
✔ The solution: `int32` as a first-class type
No annotations. No confusing
as
syntax. No ambiguity.This scales perfectly:
These are distinct types. A variable can be
int32
and similar:
This makes serialization predictable, structured, and error-free.
✔ What about overflow?
A reasonable question: what happens if a value exceeds the limit?
Answer: no runtime overflow or clamping! It's just int at TVM.
* arithmetic works normally – v becomes 256
* no extra gas cost – no runtime bounds checks
* overflow will only happen at serialization
✔ Why is this the best approach?
Think of smart contracts as a black box:
- inputs are encoded (int32, uint64, etc.)
- inside the contract, arithmetic uses full 257-bit precision
- outputs are serialized again — overflow happens only at this stage
This is similar to how mulDivFloor(x,y,z) uses 513-bit precision internally. Your contract keeps precision internally and only enforces constraints at the border with an outside world.
🌳 Tolk will follow a type-based philosophy
This post covered the foundation of automatic serialization. The right way is to have a rich type system. Having nested types, having generics, having aliases — will allow to describe every practical TL/B case, but at a language level.
In v0.10, we introduce
intN
(fixed integers), bytesN
(definite slices), coins
(variadic integers), and some more additions. Read the details in the PR.How will
Either L R
and even more complex TL/B structures be expressed? Stay tuned for the next update...
28.01.202509:12
The Hindley-Milner type system, and Why Tolk decided to avoid it
You know, that FunC is "functional C". But do you know, what makes it "functional"? Not its low-level nature. Not its peculiar syntax. And even not the
The Hindley-Milner type system is a common approach for functional languages, where types are inferred from usage through unification. As a result, type declarations are not necessary:
For example,
For example,
This "unification", looking pretty at first glance, arises problems, if we actually do not want types to unify. Imagine, we want to have nullable types:
Instead of an error, Hindley-Milner would perform unification and result in
A fun fact: you don't notice these problems in FunC. Because FunC's type system is very limited. But Tolk will have bool, fixed-width integers, nullability, smart casts, structures, and generics — these problems will become significant. Hindley-Milner will clash with structure methods, struggle with proper generics, and become entirely impractical for union types (despite theoretical claims that it was "designed for union types").
The goal is to have predictable, explicit, and positionally-checked static typing. While Hindley-Milner is powerful, it's actually "type inference for the poor" — simple to implement when there's no time to fundamentally design the language.
By the way, unreadable type errors also stem from Hindley-Milner:
What the programmer actually wants to see is:
That's why Tolk v0.7 contains a fully rewritten type system, encoupled with clear error messages and an IDE plugin with type inference included. It's the groundwork for future enhancements.
You know, that FunC is "functional C". But do you know, what makes it "functional"? Not its low-level nature. Not its peculiar syntax. And even not the
~
tilda. "Functional" is mostly about the Hindley-Milner type system.The Hindley-Milner type system is a common approach for functional languages, where types are inferred from usage through unification. As a result, type declarations are not necessary:
For example,
For example,
This "unification", looking pretty at first glance, arises problems, if we actually do not want types to unify. Imagine, we want to have nullable types:
int
(not nullable) and int?
(nullable), so that we can assign null
only to int?
. What would Hindley-Milner think about this?
var x = 0; // unify(Hole, Int) = Int
...
x = null; // unify(Int, Nullable) = Nullable
Instead of an error, Hindley-Milner would perform unification and result in
x: int?
. Not as we wanted to, right? (while it can be "fixed", it would step away from HM's nature)A fun fact: you don't notice these problems in FunC. Because FunC's type system is very limited. But Tolk will have bool, fixed-width integers, nullability, smart casts, structures, and generics — these problems will become significant. Hindley-Milner will clash with structure methods, struggle with proper generics, and become entirely impractical for union types (despite theoretical claims that it was "designed for union types").
The goal is to have predictable, explicit, and positionally-checked static typing. While Hindley-Milner is powerful, it's actually "type inference for the poor" — simple to implement when there's no time to fundamentally design the language.
By the way, unreadable type errors also stem from Hindley-Milner:
error: function return type (int, int) cannot be unified with implicit end-of-block return type (int, ()): cannot unify type () with int
What the programmer actually wants to see is:
1) can not assign `(int, slice)` to variable of type `(int, int)`
2) can not call method for `builder` with object of type `int`
3) missing `return`
That's why Tolk v0.7 contains a fully rewritten type system, encoupled with clear error messages and an IDE plugin with type inference included. It's the groundwork for future enhancements.
07.03.202509:12
𒀟 Tolk v0.9: nullable types, null safefy, control flow, smart casts
Tolk v0.9 introduces nullable types:
✅ Notable changes in Tolk v0.9:
1. Nullable types
2. Standard library updated to reflect nullability
3. Smart casts, like in TypeScript in Kotlin
4. Operator
5. Code after
6. The
PR on GitHub with detailed info.
✔ Nullable types and null safety
In FunC,
Tolk now forces you to explicitly mark nullable values. This aligns with TypeScript
* any type can be nullable:
* no more unexpected TVM exceptions due to null
* at runtime,
✔ Smart casts (via control flow graph)
Once a nullable value is checked, the compiler automatically refines its type:
or:
or:
Smart casts ensure code is safe while remaining gas-efficient (compile-time only).
✔ Operator `!` (non-null assertion)
If you know a value can't be null, use the
It's useful for low-level TVM functions (dicts, particularly), when you have guarantees outside the code. Use with care!
✔ The `never` type
Now, you can declare "always-throwing functions":
... this is just the beginning! Nullable tensors, tricky smart casts, and low-level null safety details — all explained in the PR.
🌳 Null safety is smooth, intuitive, and enforced at compile time — no runtime cost, no extra gas, just safer code.
Tolk v0.9 introduces nullable types:
int?
, cell?
, and T?
, bringing null safety to your code. The compiler prevents using nullable values without checks, but thanks to smart casts, this feels smooth and natural.✅ Notable changes in Tolk v0.9:
1. Nullable types
int?
, cell?
, etc.; null safety2. Standard library updated to reflect nullability
3. Smart casts, like in TypeScript in Kotlin
4. Operator
!
(non-null assertion)5. Code after
throw
is treated unreachable6. The
never
typePR on GitHub with detailed info.
✔ Nullable types and null safety
In FunC,
null
was implicitly assignable to any primitive type — too permissive. A variable declared as int
could still hold null
at runtime, leading to TVM exceptions if used incorrectly.Tolk now forces you to explicitly mark nullable values. This aligns with TypeScript
T | null
and Kotlin, preventing unintended null usage.
* any type can be nullable:
cell?
, [int, slice]?
, (int, cell)?
* no more unexpected TVM exceptions due to null
* at runtime,
int?
and cell?
occupy just one stack slot — zero overhead✔ Smart casts (via control flow graph)
Once a nullable value is checked, the compiler automatically refines its type:
or:
or:
Smart casts ensure code is safe while remaining gas-efficient (compile-time only).
✔ Operator `!` (non-null assertion)
If you know a value can't be null, use the
!
operator to bypass nullability checks:
// this key 100% exists, make it `cell`, not `cell?`
validators = getBlockchainConfigParam(16)!;
It's useful for low-level TVM functions (dicts, particularly), when you have guarantees outside the code. Use with care!
✔ The `never` type
Now, you can declare "always-throwing functions":
never
also occurs implicitly when a condition is impossible:
... this is just the beginning! Nullable tensors, tricky smart casts, and low-level null safety details — all explained in the PR.
🌳 Null safety is smooth, intuitive, and enforced at compile time — no runtime cost, no extra gas, just safer code.
11.02.202509:12
Tolk v0.8: preparation for structures
A new version of Tolk was released several days ago. We're starting a way to eventually implement structures with auto packing to/from cells. This will take several steps (each publicly released), it's the first one.
✅ Notable changes in Tolk v0.8:
1. Syntax
2. Allow
PR on GitHub with detailed info.
Using syntax `tensorVar.{i}` and `tupleVar.{i}`, you can access tensors/tuples by indices without unpacking them.
It works for tensors:
It works for tuples (does asm INDEX/SETINDEX under the hood):
It works for nesting
Why is this essential?
In the future, we'll have structures, declared like this:
Structures will be stored like tensors on a stack:
It means, that `obj.{field}` is exactly the same as `tensorVar.{i}`:
Same goes for nested objects:
So, implementing indexed access for tensors/tuples covering all scenarios is a direct step towards structures.
A new version of Tolk was released several days ago. We're starting a way to eventually implement structures with auto packing to/from cells. This will take several steps (each publicly released), it's the first one.
✅ Notable changes in Tolk v0.8:
1. Syntax
tensorVar.0
and tupleVar.0
, both for reading and writing2. Allow
cell
, slice
, etc. to be valid identifiersPR on GitHub with detailed info.
Using syntax `tensorVar.{i}` and `tupleVar.{i}`, you can access tensors/tuples by indices without unpacking them.
It works for tensors:
It works for tuples (does asm INDEX/SETINDEX under the hood):
It works for nesting
var.{i}.{j}
. It works for nested tensors, nested tuples, tuples nested into tensors. It works for mutate
. It works for globals.Why is this essential?
In the future, we'll have structures, declared like this:
struct User {
id: int;
name: slice;
}
Structures will be stored like tensors on a stack:
It means, that `obj.{field}` is exactly the same as `tensorVar.{i}`:
Same goes for nested objects:
struct Storage {
lastUpdated: int;
owner: User;
}
s.lastUpdated // s.0
s.owner.id // s.1.0
So, implementing indexed access for tensors/tuples covering all scenarios is a direct step towards structures.
Ko'rsatilgan 1 - 6 dan 6
Ko'proq funksiyalarni ochish uchun tizimga kiring.