diff --git a/pages/book/_meta.js b/pages/book/_meta.js index 41ad513a..e6586cba 100644 --- a/pages/book/_meta.js +++ b/pages/book/_meta.js @@ -9,14 +9,20 @@ export default { }, types: 'Type system overview', integers: 'Integers', + maps: 'Maps', + 'structs-and-messages': 'Structs and Messages', + optionals: 'Optionals', + // <- a place for https://github.com/tact-lang/tact-docs/issues/115 + brief info on comments + '-- 2': { + type: 'separator', + title: 'Expressiveness', + }, operators: 'Operators', expressions: 'Expressions', statements: 'Statements', constants: 'Constants', functions: 'Functions', - 'composite-types': 'Composite types', - // <- a place for https://github.com/tact-lang/tact-docs/issues/115 - '-- 2': { + '-- 3': { type: 'separator', title: 'Communication', }, @@ -29,7 +35,7 @@ export default { lifecycle: 'Message lifecycle', send: 'Sending messages', 'message-mode': 'Message mode', - '-- 3': { + '-- 4': { type: 'separator', title: 'Going places', }, diff --git a/pages/book/cookbook.mdx b/pages/book/cookbook.mdx index ccaa7de5..fed13fa2 100644 --- a/pages/book/cookbook.mdx +++ b/pages/book/cookbook.mdx @@ -137,7 +137,7 @@ if (first == null) { **Useful links:**\ - [`map{:tact}` type in the Book](/book/types#maps) + [`map{:tact}` type in the Book](/book/maps) diff --git a/pages/book/expressions.mdx b/pages/book/expressions.mdx index 07c79fb0..34230808 100644 --- a/pages/book/expressions.mdx +++ b/pages/book/expressions.mdx @@ -92,15 +92,15 @@ if (var != null) { } ``` -Read more about working with `null{:tact}` in the dedicated chapter: [Optionals](/book/composite-types#optionals). +Read more about working with `null{:tact}` on the dedicated page: [Optionals](/book/optionals). ## Identifiers -An identifier is a sequence of characters in the code that _identifies_ a [variable](/book/statements#let), [constant](/book/constants), [map](/book/types#maps) and a [function](/book/functions), as well as a [Struct][s], [Message][m], [contract](/book/types#contract), [trait](/book/types#trait), or their fields and methods. Identifiers are case-sensitive and not quoted. +An identifier is a sequence of characters in the code that _identifies_ a [variable](/book/statements#let), [constant](/book/constants), [map](/book/maps) and a [function](/book/functions), as well as a [Struct][s], [Message][m], [contract](/book/types#contract), [trait](/book/types#trait), or their fields and methods. Identifiers are case-sensitive and not quoted. In Tact, identifiers can contain latin lowercase letters (`a-z`), latin uppercase letters (`A-Z`), underscores (`_`) and digits ($\mathrm{0 - 9}$), but may not start with a digit. An identifier differs from a [string](#string-literals) in that a string is data, while an identifier is part of the code. -Note, that when identifiers for [primitive types][p] start with an uppercase letter. Used-defined [composite types](/book/composite-types), such as [Structs][s] and [Messages][m] also must be capitalized. +Note, that when identifiers for [primitive types][p] start with an uppercase letter. Used-defined [composite types](/book/types#composite-types), such as [Structs][s] and [Messages][m] also must be capitalized. ## Instantiation @@ -189,5 +189,5 @@ Field | Type | Description `data` | [`Cell{:tact}`][p] | initial data of the contract (arguments of `init(){:tact}` function of the contract) [p]: /book/types#primitive-types -[s]: /book/composite-types#structs -[m]: /book/composite-types#messages \ No newline at end of file +[s]: /book/structs-and-messages#structs +[m]: /book/structs-and-messages#messages \ No newline at end of file diff --git a/pages/book/func.mdx b/pages/book/func.mdx index c44f8fb5..09137b10 100644 --- a/pages/book/func.mdx +++ b/pages/book/func.mdx @@ -10,7 +10,7 @@ All rules about copying variables are the same. One of the big differences is th ## Convert serialization -Serialization of [Structs](/book/composite-types#structs) and [Messages](/book/composite-types#messages) in Tact is automatic, unlike FunC where you need to define serialization logic manually. +Serialization of [Structs](/book/structs-and-messages#structs) and [Messages](/book/structs-and-messages#messages) in Tact is automatic, unlike FunC where you need to define serialization logic manually. Tact's auto-layout algorithm is greedy. This means that it takes the next variable, calculates its size, and tries to fit it into a current cell. If it doesn't fit, it creates a new cell and continues. All inner structs for auto-layout are flattened before allocation. @@ -174,7 +174,7 @@ get fun seqno(): Int { In FunC there is a difference between tensor type `(int, int){:func}` and `(int, (int)){:func}`, but for TVM there are no differences, they all represent a stack of two integers. -To convert the tensor that returned from a FunC `get`-method, you need to define a [Struct](/book/composite-types#structs) that has the same field types as the tensor and in the same order. +To convert the tensor that returned from a FunC `get`-method, you need to define a [Struct](/book/structs-and-messages#structs) that has the same field types as the tensor and in the same order. The following code in FunC: @@ -230,7 +230,7 @@ contract StatefulContract { ### Mixed tuple and tensor return types -When some of the tensors are a tuple, you need to define a struct as in previous steps and the tuple one must be defined as a separate [Struct](/book/composite-types#structs). +When some of the tensors are a tuple, you need to define a struct as in previous steps and the tuple one must be defined as a separate [Struct](/book/structs-and-messages#structs). The following code in FunC: @@ -262,7 +262,7 @@ contract StatefulContract { ### Arguments mapping -Conversion of `get`-methods arguments is straightforward. Each argument is mapped _as-is_ to FunC one, and each tuple is mapped to a [Struct](/book/composite-types#structs). +Conversion of `get`-methods arguments is straightforward. Each argument is mapped _as-is_ to FunC one, and each tuple is mapped to a [Struct](/book/structs-and-messages#structs). The following code in FunC: diff --git a/pages/book/integers.mdx b/pages/book/integers.mdx index a1c2c60e..79312610 100644 --- a/pages/book/integers.mdx +++ b/pages/book/integers.mdx @@ -67,7 +67,7 @@ contract SerializationExample { } ``` -Integer serialization is also available for the fields of [Structs](/book/composite-types#structs) and [Messages](/book/composite-types#structs), as well as in key/value types of [maps](/book/types#maps): +Integer serialization is also available for the fields of [Structs](/book/structs-and-messages#structs) and [Messages](/book/structs-and-messages#messages), as well as in key/value types of [maps](/book/maps): ```tact struct StSerialization { diff --git a/pages/book/maps.mdx b/pages/book/maps.mdx new file mode 100644 index 00000000..7b018564 --- /dev/null +++ b/pages/book/maps.mdx @@ -0,0 +1,43 @@ +# Maps + +import { Callout } from 'nextra/components' + +The type `map{:tact}` is used as a way to associate keys of type `k` with corresponding values of type `v`. + +Possible key types: + +* [`Int{:tact}`][ints] +* `Address{:tact}` + +Possible value types: + +* [`Int{:tact}`][ints] +* [`Bool{:tact}`](/book/types#booleans) +* `Cell{:tact}` +* `Address{:tact}` +* [Struct](/book/structs-and-messages#structs) +* [Message](/book/structs-and-messages#messages) + +For example, `map{:tact}` uses [`Int{:tact}`][ints] type for its keys and values: + +```tact +struct IntToInt { + counters: map; +} +``` + +Additionally, maps allow [integer serialization](/book/integers#serialization-types) of their keys, values or both to [preserve space and reduce storage costs](/book/integers#serialization): + +```tact +struct SerializedMapInside { + countersButCompact: map; +} +``` + + + + Read about other serialization options: [Compatibility with FunC](/book/func#convert-serialization) + + + +[ints]: /book/integers diff --git a/pages/book/operators.mdx b/pages/book/operators.mdx index 0f358ec9..735004e7 100644 --- a/pages/book/operators.mdx +++ b/pages/book/operators.mdx @@ -8,7 +8,7 @@ This page lists all the operators in Tact in decreasing order of their [preceden - Note, that there are no implicit type conversions in Tact, so operators can't be used to, say, add values of different type or compare them in terms of equality without explicitly casting to the same type. That's done with certain functions from the standard library. See [`Int.toString(){:tact}`](https://docs.tact-lang.org/language/ref/strings#inttostring) for an example of such function. + Note, that there are no implicit type conversions in Tact, so operators can't be used to, say, add values of different type or compare them in terms of equality without explicitly casting to the same type. That's done with certain functions from the standard library. See [`Int.toString(){:tact}`](/language/ref/strings#inttostring) for an example of such function. @@ -51,7 +51,7 @@ Unary double-exclamation mark (_non-null assertion_) operator `!!{:tact}` is a p - Read more about optional variables and fields here: [Book→Composite Types](https://docs.tact-lang.org/book/composite-types#optionals) + Read more about optional variables and fields here: [Optionals](/book/optionals) @@ -135,7 +135,7 @@ two % 1; // 1 -1 % -5; // -1 ``` -The simplest way to avoid confusion between the two is to prefer using positive values via [`abs(x: Int){:tact}`](https://docs.tact-lang.org/language/ref/math#abs): +The simplest way to avoid confusion between the two is to prefer using positive values via [`abs(x: Int){:tact}`](/language/ref/math#abs): ```tact abs(-1) % abs(-5); // 1 @@ -297,7 +297,7 @@ Can be applied to values of following types: * [`Cell{:tact}`](/book/types#primitive-types), implicitly compares via `.hash(){:tact}` * [`Slice{:tact}`](/book/types#primitive-types), implicitly compares via `.hash(){:tact}` * [`String{:tact}`](/book/types#primitive-types) -* [`map{:tact}`](/book/types#maps), but only if their key and value types are identical +* [`map{:tact}`](/book/maps), but only if their key and value types are identical ```tact // Int: @@ -344,7 +344,7 @@ Can be applied to values of following types: * [`Cell{:tact}`](/book/types#primitive-types), implicitly compares via `.hash(){:tact}` * [`Slice{:tact}`](/book/types#primitive-types), implicitly compares via `.hash(){:tact}` * [`String{:tact}`](/book/types#primitive-types) -* [`map{:tact}`](/book/types#maps), but only if their key and value types are identical +* [`map{:tact}`](/book/maps), but only if their key and value types are identical ```tact // Int: @@ -480,7 +480,7 @@ true ? (false ? 1 : 2) : 3; // 2 ## Assignment, `=` [#assignment] -Assignment operator `={:tact}` is used to assign a value to a variable, or to a property of a [Message](/book/composite-types#messages) or a [Struct](/book/composite-types#structs). The assignent is a statement and it doesn't return a value. +Assignment operator `={:tact}` is used to assign a value to a variable, or to a property of a [Message](/book/structs-and-messages#messages) or a [Struct](/book/structs-and-messages#structs). The assignment is a statement and it doesn't return a value. ```tact let someVar: Int = 5; // assignment operator = is used here... diff --git a/pages/book/optionals.mdx b/pages/book/optionals.mdx new file mode 100644 index 00000000..e3a952fe --- /dev/null +++ b/pages/book/optionals.mdx @@ -0,0 +1,42 @@ +# Optionals + +import { Callout } from 'nextra/components' + +As it was mentioned in [type system overview](/book/types#optionals), all [primitive types](/book/types#primitive-types), [Structs](/book/structs-and-messages#structs) and [Messages](/book/structs-and-messages#messages) could be nullable. That is, they don't necessarily hold any value, aside from `null{:tact}` — a special value, which represents the intentional absence of any other value. + +[Variables](/book/statements#let) or fields of [Structs](/book/structs-and-messages#structs) and [Messages](/book/structs-and-messages#messages) that can hold `null{:tact}` are called "optionals". They're useful to reduce state size when the variable isn't necesserily used. + +You can make any variable or a field an optional by adding a question mark (`?{:tact}`) after its type declaration. The only exceptions are [`map{:tact}`](/book/maps) and [`bounced{:tact}`](/book/bounced#bounced-messages-in-tact), where you can't make them, inner key/value type (in case of a map) or the inner [Message](/book/structs-and-messages#messages) (in case of a bounced) optional. + +Optional variables or optional fields that are not defined hold the `null{:tact}` value by default. You cannot access them without checking for `null{:tact}` first. But if you're certain they are not `null{:tact}` at a given moment, use the [non-null assertion operator `!!{:tact}`](/book/operators#unary-non-null-assert) to access their value. + +Trying to access the value of an optional variable or an optional field without using [`!!{:tact}`](/book/operators#unary-non-null-assert) or without checking for `null{:tact}` beforehand will result in a compilation error. + +Example of optionals: + +```tact +struct StOpt { + opt: Int?; // Int or null +} + +message MsOpt { + opt: StOpt?; // Notice, how the struct StOpt is used in this definition +} + +contract Optionals { + opt: Int?; + address: Address?; + + init(opt: Int?) { // optionals as parameters + self.opt = opt; + self.address = null; // explicit null value + } + + receive(msg: MsOpt) { + let opt: Int? = 12; // defining a new variable + if (self.opt != null) { // explicit check + self.opt = opt!!; // using !! as we know that opt value isn't null + } + } +} +``` \ No newline at end of file diff --git a/pages/book/composite-types.mdx b/pages/book/structs-and-messages.mdx similarity index 56% rename from pages/book/composite-types.mdx rename to pages/book/structs-and-messages.mdx index 4300b610..9bee0317 100644 --- a/pages/book/composite-types.mdx +++ b/pages/book/structs-and-messages.mdx @@ -1,14 +1,12 @@ -# Composite types +# Structs and Messages import { Callout } from 'nextra/components' -Tact supports a number of [primitive data types](/book/types#primitive-types) that are tailored for smart contract use. However, using individual means of storage often becomes cumbersome, so there are two main ways to combine multiple primitives together: [Structs](#structs) and [Messages](#messages). - -Note, while Traits and Contracts are also considered a part of the Tacts type system, one can't pass them around like [Structs](#structs) or [Messages](#messages). Instead, one can obtain the initial state of the given Contract by using the [`initOf{:tact}`](/book/expressions#initof) expression. +Tact supports a number of [primitive data types](/book/types#primitive-types) that are tailored for smart contract use. However, using individual means of storage often becomes cumbersome, so there are [Structs](#structs) and [Messages](#messages) which allow combining types together. - **Warning**: Currently circular types are **not** possible. This means that struct/message **A** can't have a field of a struct/message **B** that has a field of the struct/message **A**. + **Warning**: Currently circular types are **not** possible. This means that Struct/Message **A** can't have a field of a Struct/Message **B** that has a field of the Struct/Message **A**. Therefore, the following code **won't** compile: @@ -105,44 +103,3 @@ This is useful for cases where you want to handle certain opcodes (operation cod [Jetton Standard in Tact on Tact-by-Example](https://tact-by-example.org/07-jetton-standard) - -## Optionals - -As it was mentioned in [type system overview](/book/types), most [primitive types](/book/types#primitive-types), [Structs](#structs) and [Messages](#messages) could be nullable. That is, they don't necessarily hold any value, aside from `null{:tact}` — a special value, which represents the intentional absence of any other value. - -[Variables](/book/statements#variable-declaration) or fields of [Structs](#structs) and [Messages](#messages) that can hold `null{:tact}` are called "optionals". They're useful to reduce state size when the variable isn't necesserily used. - -You can make any variable or a field an optional by adding a question mark (`?{:tact}`) after its type declaration. The only exceptions are [`map<>{:tact}`](/book/types#maps) and [`bounced<>{:tact}`](/book/bounced#bounced-messages-in-tact), where you can't make them, inner key/value type (in case of a map) or the inner [Message](#messages) (in case of a bounced) optional. - -Optional variables or optional fields that are not defined hold the `null{:tact}` value by default. You cannot access them without checking for `null{:tact}` first. But if you're certain they are not `null{:tact}` at a given moment, use the non-null assertion operator (`!!{:tact}`) to access their value. - -Trying to access the value of an optional variable or an optional field without using `!!{:tact}` or without checking for `null{:tact}` beforehand will result in a compilation error. - -Example of optionals: - -```tact -struct StOpt { - opt: Int?; // Int or null -} - -message MsOpt { - opt: StOpt?; // Notice, how the struct StOpt is used in this definition -} - -contract Optionals { - opt: Int?; - address: Address?; - - init(opt: Int?) { // optionals as parameters - self.opt = opt; - self.address = null; // explicit null value - } - - receive(msg: MsOpt) { - let opt: Int? = 12; // defining a new variable - if (self.opt != null) { // explicit check - self.opt = opt!!; // using !! as we know that opt value isn't null - } - } -} -``` \ No newline at end of file diff --git a/pages/book/types.mdx b/pages/book/types.mdx index 782cbdce..8b588922 100644 --- a/pages/book/types.mdx +++ b/pages/book/types.mdx @@ -5,24 +5,26 @@ import { Callout } from 'nextra/components' Every variable, item, and value in Tact programs has a type. They can be: * One of the [primitive types](#primitive-types) -* [Maps](#maps) -* Composite types, such as [Structs and Messages](#structs-and-messages) -* or [Contracts](#contracts) and [Traits](#traits) +* or [composite types](#composite-types) -Also, many of those types [can be made nullable](/book/composite-types#optionals). +Additionally, many of those types [can be made nullable](#optionals). ## Primitive types -* [`Int{:tact}`][int] — all numbers in Tact are $257$-bit signed integers, but [smaller representations](/book/integers#serialization) can be used to reduce storage costs. +Tact supports a number of primitive data types that are tailored for smart contract use: + +* [`Int{:tact}`][ints] — all numbers in Tact are $257$-bit signed integers, but [smaller representations](/book/integers#serialization) can be used to reduce storage costs. * [`Bool{:tact}`](#booleans) — classical boolean with `true{:tact}` and `false{:tact}` values. * `Address{:tact}` — standard [smart contract address](https://docs.ton.org/learn/overviews/addresses#address-of-smart-contract) in TON Blockchain. * `Slice{:tact}`, `Cell{:tact}`, `Builder{:tact}` — low-level primitives of TON VM. * `String{:tact}` — represents text strings in TON VM. * `StringBuilder{:tact}` — helper type that allows you to concatenate strings in a gas-efficient way. -## Booleans +[ints]: /book/integers + +### Booleans [#booleans] -The primitive type `Bool{:tact}` can hold only the two values: `true{:tact}` and `false{:tact}`. It's convenient for boolean and logical operations, as well as for storing flags. +The primitive type `Bool{:tact}` is the classical boolean type, which can hold only the two values: `true{:tact}` and `false{:tact}`. It's convenient for boolean and logical operations, as well as for storing flags. There are no implicit type conversions in Tact, so addition (`+{:tact}`) of two boolean values isn't possible. Hovewer, many comparison [operators](/book/statements#operators) are available, such as: @@ -31,53 +33,35 @@ There are no implicit type conversions in Tact, so addition (`+{:tact}`) of two * `!{:tact}` for [logical inversion](/book/operators#unary-inverse), * `=={:tact}` for checking [equality](/book/operators#binary-equal), * `!={:tact}` for checking [inequality](/book/operators#binary-not-equal), -* and `!!{:tact}` for [non-null assertion](/book/composite-types#optionals). +* and `!!{:tact}` for [non-null assertion](/book/optionals). Persisting bools to state is very space-efficient, as they only take 1-bit. Storing 1000 bools in state [costs](https://ton.org/docs/develop/smart-contracts/fees#how-to-calculate-fees) about $0.00072$ TON per year. -## Maps - -The type `map{:tact}` is used as a way to associate values of type `v` with corresponding keys of type `k`. +## Composite types -Possible key types: +Using individual means of storage often becomes cumbersome, so there are ways to combine multiple [primitive types](#primitive-types) together to create composite types: -* [`Int{:tact}`][int] -* `Address{:tact}` +* [Maps](#maps) — associations of keys with values. +* [Structs and Messages](#structs-and-messages) — data structures with typed fields. +* [Optionals](#optionals) — `null{:tact}` values for variables or fields of [Structs and Messages](#structs-and-messages). -Possible value types: +Note, while [contracts](#contracts) and [traits](#traits) are also considered a part of the Tacts type system, one can't pass them around like [Structs and Messages](#structs-and-messages). Instead, it's possible to obtain the initial state of the given contract by using the [`initOf{:tact}`](/book/expressions#initof) expression. -* [`Int{:tact}`][int] -* [`Bool{:tact}`](#booleans) -* `Cell{:tact}` -* `Address{:tact}` -* [Struct](#structs-and-messages) -* [Message](#structs-and-messages) +### Maps -For example, `map{:tact}` uses [`Int{:tact}`][int] type for its keys and values: - -```tact -struct IntToInt { - counters: map; -} -``` +The type [`map{:tact}`][maps] is used as a way to associate keys of type `k` with corresponding values of type `v`. -Additionally, maps allow [integer serialization](/book/integers#serialization-types) of its keys, values or both to [preserve space and reduce storage costs](/book/integers#serialization): +Example of a [`map{:tact}`][maps]: ```tact -struct SerializedMapInside { - countersButCompact: map; -} +let mapExample: map = emptyMap(); // empty map with Int keys and values ``` - - - Other serialization options are available too: [Compatibility with FunC](/book/func#convert-serialization) - - +Learn more about them on a dedicated page: [Maps][maps]. -[int]: /book/integers +[maps]: /book/maps -## Structs and Messages +### Structs and Messages [Structs][structs] and [Messages][messages] are two main ways of combining multiple [primitive types](#primitive-types) into a composite one. @@ -101,12 +85,27 @@ message(0x11111111) SetValue { } ``` -Learn more about them on a dedicated page about [composite types](/book/composite-types). +Learn more about them on a dedicated page: [Structs and Messages][s-n-m]. -[structs]: /book/composite-types#structs -[messages]: /book/composite-types#messages +[s-n-m]: /book/structs-and-messages +[structs]: /book/structs-and-messages#structs +[messages]: /book/structs-and-messages#messages -## Contracts +### Optionals + +All [primitive types](#primitive-types), as well as [Structs and Messages](#structs-and-messages) could be nullable and hold a special `null{:tact}` value. + +Example of an [optional][optionals]: + +```tact +let opt: Int? = null; // Int or null, with explicitly assigned null +``` + +Learn more about them on a dedicated page: [Optionals][optionals]. + +[optionals]: /book/optionals + +### Contracts Contracts are the main entry of a smart contract on the TON blockchain. It holds all [functions](/book/functions), [getters](/book/functions#getter-functions), and [receivers](/book/functions#receiver-functions) of a TON contract. @@ -131,7 +130,7 @@ contract HelloWorld { } ``` -## Traits +### Traits Tact doesn't support classical class inheritance, but instead introduces the concept of **traits**. Trait defines functions, receivers, and required fields. The trait is like abstract classes, but it does not define how and where fields must be stored. **All** fields from all traits must be explicitly declared in the contract itself. Traits themselves don't have `init(){:tact}` constructors, so all initial field initialization also must be done in the main contract. @@ -153,11 +152,11 @@ And the contract that uses trait: ```tact contract Treasure with Ownable { - owner: Address; // Field from trait MUST be defined in contract itself + owner: Address; // Field from trait MUST be defined in contract itself - // Here we init the way we need, trait can't specify how you must init owner field - init(owner: Address) { - self.owner = owner; - } + // Here we init the way we need, trait can't specify how you must init owner field + init(owner: Address) { + self.owner = owner; + } } ``` \ No newline at end of file diff --git a/scripts/redirects-generate.js b/scripts/redirects-generate.js index 3be66ffd..db0919b3 100644 --- a/scripts/redirects-generate.js +++ b/scripts/redirects-generate.js @@ -118,7 +118,12 @@ const getRedirects = () => [ { source: '/book/defining-types', subSources: undefined, - destination: '/book/composite-types', + destination: '/book/types#composite-types', + }, + { + source: '/book/composite-types', + subSources: undefined, + destination: '/book/types#composite-types', }, ];