diff --git a/README.md b/README.md index 8940df20..058c26a6 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,28 @@ The documentation pages are written in `.mdx` which are [markdown](https://www.markdownguide.org/basic-syntax/) files that can also import react components. +## Audiences for language documentation + +The programmer's tour is the complete first introduction for people who already +program. Explain Toit directly, then use C/C++, JavaScript, Python, or Java/Kotlin +comparisons where useful. Familiarity with every comparison language is not a +prerequisite. Keep essential content in one reading path; language-specific +pages highlight differences and link into it. Reference pages cover detailed +rules rather than carrying missing parts of the introduction. + +Beginner lessons assume no programming knowledge. Introduce concepts when a +concrete example needs them, with time to run and modify the program before +adding another concept. Split lessons at useful stopping points. + +## Examples in documentation + +Keep examples visible when they introduce a concept or the surrounding text +explains them. A reader should not have to expand code to follow the argument. +For experienced readers, state the rule concisely and show one or two examples. +Use `` for supplementary collections that +explore variations and edge cases, with output or comments explaining each case. +Keep exercise solutions collapsed so readers can try the exercise first. + ## Components There are multiple components to make the content more engaging: diff --git a/docs/language/beginner.mdx b/docs/language/beginner.mdx new file mode 100644 index 00000000..d74c6d41 --- /dev/null +++ b/docs/language/beginner.mdx @@ -0,0 +1,205 @@ +# Beginner tutorial + +These lessons are for someone new to programming. You will write a temperature +report using sample readings, so you need a computer but no sensor or ESP32. +If you already program, start with [Toit for programmers](/language/toitversus). + +You should be comfortable creating a text file and opening a terminal. The +terminal is where you type the command that runs your program. We explain the +programming concepts as they appear. + +| Lesson | What you will make | +| --- | --- | +| 1. Your first program, on this page | Report whether one temperature is warm. | +| [2. Lists and functions](/language/beginner/lists-and-functions) | Report several readings using one reusable operation. | +| [3. Objects](/language/beginner/objects) | Keep a room's name and readings together. Continue here when you are ready. | + +Each example is a complete program. Replace the contents of your file rather +than appending the example to the previous one. Run it after each change. + +## 1. Run your first program + +Install the [Toit SDK](https://github.com/toitlang/toit#readme), which includes +the tools to run Toit programs. Check the installation by typing `toit --help` +in a terminal. It should display instructions for the command. +If you already have Jaguar installed, use `jag toit` instead of `toit` in these +commands; you do not need to connect a device. + +In a text editor, create a file named `report.toit` containing these two lines: + +```toit +main: + print "Temperature report" +``` + +The two spaces before `print` matter. Use spaces, not the Tab key. +Save the file, open a terminal in the directory containing it, and type: + +```sh +toit run report.toit +``` + +You should see: + +```text +Temperature report +``` + +`main:` marks where the program starts. The indented line underneath it is an +instruction: `print` displays the text between the double quotes. Text written +this way is called a *string*. The quotes are part of the program, but are not +printed. + +Change `Temperature report` to `Kitchen report`, save, and run the command +again. The new message should appear. The program runs the saved version of +your file, so remember to save before running. + + + +If the terminal cannot find `toit`, revisit the SDK installation instructions. +If it cannot find `report.toit`, check that the terminal is open in the directory +where you saved the file. If Toit reports a problem in the program, start with +the first reported line. Compare the spelling, quotes, colon, and indentation +with the example. + + + +## 2. Give a value a name + +We want the report to include a temperature. We could print `21` directly, but +naming it lets us use the same value in several places. + +A *variable* is a name for a value. `temperature := 21` creates a variable named +`temperature` with the value `21`. The `:=` introduces that new name. + + + +```toit +main: + temperature := 21 + print temperature +``` + +This prints `21`. There are no quotes around `temperature`: we want its value, +not the word “temperature”. Instructions run from top to bottom, so the variable +is created before `print` uses it. + +Change `21` to `24`, save, and run again. Then try `print "temperature"` and +compare the output. Put `print temperature` back before continuing. + +## 3. Change the value + +Once a variable exists, use `=` to replace its value. Use `:=` only when +introducing the name. + + + +```toit +main: + temperature := 21 + print temperature + temperature = 24 + print temperature +``` + +The output is: + +```text +21 +24 +``` + +The first `print` uses the value at that point in the program. Changing the +variable later does not change text that has already been printed. + +You can calculate a new value from the old one. Replace `temperature = 24` +with `temperature = temperature + 3`. Toit first adds three to the current +value, then stores the result. The output should stay the same. + +Try adding five instead. Predict the second line before running the program. + +## 4. Put the value in a message + +To print a label and a value together, put `$temperature` inside a string. +Toit replaces it with the variable's value. This is called *interpolation*. + + + +```toit +main: + temperature := 21 + print "Temperature: $temperature C" +``` + +This prints: + +```text +Temperature: 21 C +``` + +Change the value to `24`. You only need to change the line that sets the +temperature; the message uses that value automatically. + +## 5. Decide what to print + +Our program should say “Warm” when the temperature is at least 25 degrees. +Otherwise, it should say “Comfortable”. The question “is this temperature at +least 25?” is written as `temperature >= 25`. + + + +```toit +main: + temperature := 24 + print "Temperature: $temperature C" + if temperature >= 25: + print "Warm" + else: + print "Comfortable" +``` + +This prints: + +```text +Temperature: 24 C +Comfortable +``` + +`if` asks the question. When the answer is yes, the program runs the further +indented instruction below it. When the answer is no, it runs the instruction +below `else:` instead. Only one of the two messages is printed. + +The comparison is called a *condition*. Its result is `true` for yes or `false` +for no. Those two values are called *booleans*. + +Try temperatures of `25` and `30`. Both should print `Warm`. Try `20`, which +should print `Comfortable`. You have now checked values below, at, and above +the threshold. + +## Practice before continuing + +Change the report so that it describes a freezer: print `Freezing` when the +temperature is below zero, and `Not freezing` otherwise. Use `<` for “less than”. +Check the program with `-5`, `0`, and `5`. + + + + + +```toit +main: + temperature := -5 + if temperature < 0: + print "Freezing" + else: + print "Not freezing" +``` + +This prints `Freezing`. With `0` or `5`, it prints `Not freezing`. + + + +You have a complete program that stores a reading, includes it in a message, +and chooses a description. Continue with +[lists and functions](/language/beginner/lists-and-functions) to report several +readings without copying the same instructions for each one. diff --git a/docs/language/beginner/lists-and-functions.mdx b/docs/language/beginner/lists-and-functions.mdx new file mode 100644 index 00000000..80e39892 --- /dev/null +++ b/docs/language/beginner/lists-and-functions.mdx @@ -0,0 +1,215 @@ +# Lists and functions + +This is lesson 2 of the [beginner tutorial](/language/beginner). It assumes you +have run the first lesson's examples and can use a variable, print a string, +and make a decision with `if`. + +We will first report several temperatures, then give the reporting operation a +name so we can reuse it. Keep using `report.toit`; each example replaces the +whole file. + +## Store several readings + +A *list* holds values in order. Square brackets surround its values, and commas +separate them. Here is a list containing three temperatures: + +```toit +main: + readings := [21, 26, 24] + print readings +``` + +This prints `[21, 26, 24]`. Add `28` after `24`, separated by a comma, and run +it again. You should see four values in the printed list. + +## Do something for each reading + +We want to print each temperature on its own line. `readings.do:` runs a piece +of code once for each value in the list: + + + +```toit +main: + readings := [21, 26, 24] + readings.do: |temperature| + print "Temperature: $temperature C" +``` + +The output is: + +```text +Temperature: 21 C +Temperature: 26 C +Temperature: 24 C +``` + +`|temperature|` gives a name to the value for the current run. On the first run +it is `21`, on the second `26`, and on the third `24`. The indented code after +`do:` is called a *block*. Here that block contains one instruction. + +Add another reading and check that it produces another output line. Try `[]`, +an empty list. There are no values to process, so the block does not run and +nothing is printed. + +## Add a decision for each reading + +The block can contain several instructions. Put the first lesson's decision +inside it: + + + +```toit +main: + readings := [21, 26, 24] + readings.do: |temperature| + if temperature >= 25: + print "$temperature C: Warm" + else: + print "$temperature C: Comfortable" +``` + +Expected output: + +```text +21 C: Comfortable +26 C: Warm +24 C: Comfortable +``` + +Notice the indentation. The `if` and `else` belong to the block. Their `print` +instructions are indented one level further, because each belongs to one side +of the decision. + +Try `[25, 10]`. Predict both messages, then run the program. + +## Give the operation a name + +We might want to report a temperature from somewhere other than this list. +A *function* lets us name an operation and call it wherever we need it. + +We already have a function, `main`. Add another called `print-reading`: + + + +```toit +print-reading temperature: + if temperature >= 25: + print "$temperature C: Warm" + else: + print "$temperature C: Comfortable" + +main: + print-reading 21 + print-reading 26 +``` + +This prints: + +```text +21 C: Comfortable +26 C: Warm +``` + +The two lines under `main` *call* the function. Each call runs its body using +the supplied temperature. In `print-reading 21`, the supplied value `21` is +an *argument*. The name `temperature` in the function definition is a *parameter*. + +`print-reading` contains a hyphen because that is a normal part of a Toit name. +The function definitions start at the left edge of the file; their bodies are +indented. Defining `print-reading` does not run it. A call runs it. + +Add `print-reading 25` under `main` and check the result. + +## Combine the function and the list + +The list block can call the function for every reading. The function keeps +the reporting instructions in one place: + + + +```toit +print-reading temperature: + if temperature >= 25: + print "$temperature C: Warm" + else: + print "$temperature C: Comfortable" + +main: + readings := [21, 26, 24] + readings.do: |temperature| + print-reading temperature +``` + +The output is the same three-line report as before. To change how temperatures +are reported, edit `print-reading`. To change which temperatures are reported, +edit the list in `main`. + +## Pass a second argument + +Different reports may use different limits. Add a second parameter, `limit`, +so callers can choose it: + + + +```toit +print-reading temperature limit: + if temperature >= limit: + print "$temperature C: Warm" + else: + print "$temperature C: Comfortable" + +main: + readings := [21, 26, 24] + readings.do: |temperature| + print-reading temperature 24 +``` + +Now both `26` and `24` are reported as warm. Spaces separate the call's arguments: +the first is the temperature, and the second is the limit. Commas belong between +the values in a list, not between a function's arguments. + +## Practice + +Create two lists named `kitchen` and `bedroom`. Print a room heading, then report +that room's readings. Use a limit of `25` for the kitchen and `23` for the bedroom. + + + + + +```toit +print-reading temperature limit: + if temperature >= limit: + print "$temperature C: Warm" + else: + print "$temperature C: Comfortable" + +main: + kitchen := [21, 26] + bedroom := [22, 24] + print "Kitchen" + kitchen.do: |temperature| + print-reading temperature 25 + print "Bedroom" + bedroom.do: |temperature| + print-reading temperature 23 +``` + +Expected output: + +```text +Kitchen +21 C: Comfortable +26 C: Warm +Bedroom +22 C: Comfortable +24 C: Warm +``` + + + +This is a useful stopping point: you can process a collection and reuse code. +When you are ready, continue with [objects](/language/beginner/objects) to keep +each room's settings and readings together. To try the language on hardware, +follow [Run on your device](/getstarted/device). diff --git a/docs/language/beginner/objects.mdx b/docs/language/beginner/objects.mdx new file mode 100644 index 00000000..52cdbabc --- /dev/null +++ b/docs/language/beginner/objects.mdx @@ -0,0 +1,194 @@ +# Objects + +This is lesson 3 of the [beginner tutorial](/language/beginner). Start with +[lists and functions](/language/beginner/lists-and-functions) if you have not +yet written a function or used `do` to process a list. + +The previous lesson kept each room's name, readings, and warm limit separately. +We will now group them, so we can pass a room's whole report around as one value. + +## Keep related values together + +An *object* can hold several named values. These values are called its *fields*. +A *class* describes the fields its objects have and the operations they support. +Our class will be named `TemperatureReport`. + +The *constructor* runs when a new object is created. Here it receives three +arguments and stores them in the new object's fields: + +```toit +class TemperatureReport: + room := "" + readings := [] + limit := 25 + + constructor room readings limit: + this.room = room + this.readings = readings + this.limit = limit + +main: + report := TemperatureReport "Kitchen" [21, 26] 25 + print report.room + print report.readings +``` + +Expected output: + +```text +Kitchen +[21, 26] +``` + +`TemperatureReport "Kitchen" [21, 26] 25` creates an object and runs its +constructor with those three values. `this` refers to the object being created: +in `this.room = room`, the left side is its field and the right side is the +constructor's argument. `report.room` reads that object's `room` field. + +Change the room name or the readings and run again. Then create a second report +and print both room names. Each object keeps its own field values. + +## Put an operation on the object + +A function inside a class is called a *method*. It can use the object's fields, +so callers do not need to pass the room, readings, and limit each time. + +The `print-report` method below uses what we learned in the previous lessons: +printing a string, iterating over a list, and making a decision. + + + +```toit +class TemperatureReport: + room := "" + readings := [] + limit := 25 + + constructor room readings limit: + this.room = room + this.readings = readings + this.limit = limit + + print-report: + print room + readings.do: |temperature| + if temperature >= limit: + print "$temperature C: Warm" + else: + print "$temperature C: Comfortable" + +main: + kitchen := TemperatureReport "Kitchen" [21, 26] 25 + bedroom := TemperatureReport "Bedroom" [22, 24] 23 + kitchen.print-report + bedroom.print-report +``` + +Expected output: + +```text +Kitchen +21 C: Comfortable +26 C: Warm +Bedroom +22 C: Comfortable +24 C: Warm +``` + +Calling `kitchen.print-report` uses the kitchen object's fields. Calling +`bedroom.print-report` uses the bedroom object's fields. The method takes no +arguments, so its name is all we need to call it. + +## Handle a room without readings + +An empty list currently produces only the room heading. We can make the report +clearer by printing `No readings` when there are none. + +The list's `size` gives the number of values it contains. `readings.size == 0` +asks whether that number is zero. Use `==` to compare values; a single `=` is +an assignment, as in the constructor. + + + +```toit +class TemperatureReport: + room := "" + readings := [] + limit := 25 + + constructor room readings limit: + this.room = room + this.readings = readings + this.limit = limit + + print-report: + print room + if readings.size == 0: + print "No readings" + return + readings.do: |temperature| + if temperature >= limit: + print "$temperature C: Warm" + else: + print "$temperature C: Comfortable" + +main: + kitchen := TemperatureReport "Kitchen" [21, 26] 25 + bedroom := TemperatureReport "Bedroom" [] 23 + kitchen.print-report + bedroom.print-report +``` + +This prints the kitchen report, then: + +```text +Bedroom +No readings +``` + +`return` stops the method immediately and resumes execution after the call. +Here there is no reason to continue into the loop once we know the list is empty. +The kitchen report still reaches the loop because its list is not empty. + +## Practice + +Add readings to the bedroom report and check that `No readings` disappears. +Then create a third report for another room. You should only need to add its +creation and method call under `main`; the class can stay the same. + + + +Toit lets a constructor parameter initialize a field directly by prefixing its +name with a dot. This version stores the same three arguments: + + + +```toit +class TemperatureReport: + room := "" + readings := [] + limit := 25 + + constructor .room .readings .limit: + +main: + report := TemperatureReport "Kitchen" [21, 26] 25 + print report.room +``` + +The constructor body is empty because the field assignments are expressed in +its parameters. This is shorthand for the explicit assignments used above. + + + +## Continue when you need more + +You can now build a program using variables, decisions, lists, functions, +and objects. You do not need to learn every feature before using these tools. + +The [programmer's tour](/language/toitversus) introduces function results, type +annotations, collection transformations, error handling, imports, and tasks. +It assumes the concepts you have now practiced, but moves more quickly. +Use the [language reference](/language/reference) to look up an individual +feature. For hardware, continue with [Run on your device](/getstarted/device) +and the [first LED tutorial](/tutorials/hardware/led). diff --git a/docs/language/bitmask.mdx b/docs/language/bitmask.mdx index d803da83..4cf263b1 100644 --- a/docs/language/bitmask.mdx +++ b/docs/language/bitmask.mdx @@ -1,5 +1,9 @@ # Bitwise operations +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + Many devices have registers. These are a small number of binary numbers stored in the device and used to control the device or read its sensors. diff --git a/docs/language/blocks-and-lambdas.mdx b/docs/language/blocks-and-lambdas.mdx index 74cef33b..e12dde4d 100644 --- a/docs/language/blocks-and-lambdas.mdx +++ b/docs/language/blocks-and-lambdas.mdx @@ -1,5 +1,9 @@ # Blocks and Lambdas +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + Toit provides two mechanisms for creating and passing around pieces of executable code: blocks and lambdas. While they share some similarities, they have distinct characteristics, use cases, and performance implications. diff --git a/docs/language/booleans.mdx b/docs/language/booleans.mdx index d730a38e..c0b1154c 100644 --- a/docs/language/booleans.mdx +++ b/docs/language/booleans.mdx @@ -1,5 +1,9 @@ # Booleans +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + In Toit, the boolean type is `bool` and its two values are written `true` and `false`. Whenever built-in constructs need to evaluate a condition (for example in an diff --git a/docs/language/definitions.mdx b/docs/language/definitions.mdx index 4efa35ca..c9bd6ac8 100644 --- a/docs/language/definitions.mdx +++ b/docs/language/definitions.mdx @@ -1,5 +1,9 @@ # Definitions +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + ## Library A library is a code unit developers can import. There is a one-to-one relationship between a Toit file and a library. diff --git a/docs/language/exceptions.mdx b/docs/language/exceptions.mdx index 72a873c1..70b7b671 100644 --- a/docs/language/exceptions.mdx +++ b/docs/language/exceptions.mdx @@ -1,5 +1,9 @@ # Exception handling +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + ## Try and finally The `try` block in Toit is used to execute code following the `try` statement as a “normal” part of the program. diff --git a/docs/language/from-cpp.mdx b/docs/language/from-cpp.mdx new file mode 100644 index 00000000..35893709 --- /dev/null +++ b/docs/language/from-cpp.mdx @@ -0,0 +1,140 @@ +# Toit for C and C++ programmers + +For readers familiar with native code, memory management, and threads. This +page highlights the move to managed objects and cooperative tasks. For the full +language introduction, read [Toit for programmers](/language/toitversus), which +explains each feature directly and draws comparisons with C/C++, JavaScript, +Python, and Java/Kotlin. + +## Memory is managed; resources still need cleanup + +Objects are references to managed values. You do not use `malloc`, `free`, +`new`, or `delete`, or perform pointer arithmetic. Use a `ByteArray` to hold +binary data and library APIs to access hardware. + +Garbage collection reclaims unreachable objects, but its timing does not give +you C++ destructor semantics. Use `try:` with `finally:` to explicitly close a +resource. This matters when a file, socket, or peripheral must be released +before the surrounding operation returns. + +```toit +main: + error := catch: + try: + print "Using resource" + throw "Operation failed" + finally: + print "Cleanup runs here" + if error: + print "Handled: $error" +``` + +Expected output: + +```text +Using resource +Cleanup runs here +Handled: Operation failed +``` + +Replace the cleanup message with the resource API's close operation in real +code. See [exception handling](/language/exceptions). + +## Types are runtime contracts + +Annotations are optional and checked at runtime. `/int` is a signed 64-bit +integer, not a target-dependent machine `int`. `/float` is a floating-point +value. A typed variable does not accept `null` unless its type has a `?` suffix. +Analysis catches many errors, but you should not assume every type mismatch is +a compilation error. + +`:=` declares a mutable binding, `=` assigns, and `::=` declares a final binding. +Finality does not make the referenced object immutable. For fixed-width binary +protocol fields, use [byte arrays](/language/listsetmap) and encoding APIs rather +than depending on object layout or C struct packing. + +## Classes are constructed by calling them + +Toit uses indentation in place of braces. Arguments are separated by spaces, +and parentheses group nested expressions. Calling a class constructs an object: +`counter := Counter`. Zero-argument calls need no parentheses, so `counter.read` +invokes `read` rather than producing a method pointer. + +## Blocks provide scoped control flow + +Blocks are central to Toit APIs. A block can use and update the enclosing +function's locals and can return from that function. A method accepts a block +using a parameter such as `[action]`, then invokes it with `action.call`. +This lets library methods behave like control structures. + +Block references encode stack-relative positions as small integers; passing +one does not require allocating a heap closure for the captured variables. +The language enforces the corresponding lifetime: blocks cannot escape into +fields, globals, collections, or return values. C++ lambdas can also avoid heap +allocation, but Toit distinguishes blocks from longer-lived callbacks in its +parameter syntax. + +Use a lambda, `:: counter.read`, when you want to store a call for later. +Use `:` blocks for scoped iteration. A `return` inside a block returns from the +enclosing function, unlike a C++ lambda. A Toit `::` lambda uses its final +expression as its result and cannot contain an explicit `return`. + + + +```toit +first-positive values: + values.do: |value| + if value > 0: + return value + return null + +main: + print (first-positive [-1, 0, 4, 8]) +``` + +This prints `4`. The `do` method implements iteration while the block retains +the enclosing function's return behavior. The shared tour shows +[how to write block-taking methods](/language/toitversus#write-a-method-that-accepts-a-block) +and explains the lifetime and result rules in more detail. + +## Tasks cooperate within a program + +Each task has a call stack, but tasks in a program share objects and take turns +running. They switch at yielding operations, such as waiting for I/O or sleeping. +Ordinary functions can wait, so sequential code can handle concurrent activities +without a callback at every operation. + +This is different from preemptive threads. A computation that does not yield +cannot be interrupted by another task in the same program; a loop that never +yields also starves those tasks. Shared-state operations spanning a wait may +still need synchronization. Do not assume a helper cannot yield just because +its call looks ordinary. + + + +```toit +report label/string delay/int -> none: + 2.repeat: + sleep --ms=delay + print label + +main: + task:: report "Fast" 10 + task:: report "Slow" 25 +``` + +Each task prints twice. The waits let the other task progress; the exact timing +is not a real-time guarantee. See [tasks](/language/tasks). + +## Libraries replace headers + +Each `.toit` file is an importable library. There are no separate header files +for declarations. `import .helpers` imports a sibling library; SDK libraries +are imported by name. Package dependencies are resolved before execution. + +Continue with [imports](/language/imports), [packages](/language/package/pkgguide), +and the [peripheral guides](/peripherals) for device APIs. + +For additional examples of call grouping, string interpolation, defaults, and +block control flow, expand the example collections in +[Toit for programmers](/language/toitversus). diff --git a/docs/language/from-javascript.mdx b/docs/language/from-javascript.mdx new file mode 100644 index 00000000..3f4b2a2f --- /dev/null +++ b/docs/language/from-javascript.mdx @@ -0,0 +1,246 @@ +# Toit for JavaScripters + +For JavaScript and TypeScript programmers who already know functions, objects, +and asynchronous code. This page highlights assumptions to revisit when moving +to Toit. For the full language introduction, read +[Toit for programmers](/language/toitversus), which explains each feature directly +and draws comparisons with C/C++, JavaScript, Python, and Java/Kotlin. + +Each Toit example is a complete program you can run using the +[local setup](/language/beginner#1-run-your-first-program). + +## Calls do not produce function references + +`console.log(value)` becomes `print value`. A zero-argument method is invoked +by naming it: `name.trim` calls `trim`. To capture work for later, write a lambda +such as `:: name.trim`, then invoke it with `.call`. + +Use `:=` for a mutable declaration and `=` for reassignment. `::=` makes a +binding final, like `const`; it does not freeze the object. Names use +`kebab-case`, and blocks are delimited by indentation. + +```toit +main: + names ::= [" Ada "] + names.add " Grace " + show-names := :: + names.do: |name| + print name.trim + show-names.call +``` + +This prints `Ada` and `Grace` on separate lines. The final `names` binding +still refers to a mutable list. + +Named arguments are part of the signature: `sleep --ms=100`. They are not an +options object. Parentheses group nested calls, as in `print (int.parse "42")`. +See [syntax](/language/syntax). + +## Strings interpolate with `$` + +Use `"Hello $name"` or `"Hello $(name.trim)"` in place of a template literal's +`${name}` expression. Toit uses ordinary double quotes for interpolated strings. + + + +```toit +main: + name := "Ada" + print "Hello $name" // Prints Hello Ada. + print "Name length: $name.size" // Prints Name length: 3. +``` + +Dot access and indexing are included in the interpolation. Parentheses delimit +it: `"$(name).size"` produces `Ada.size`. See the +[interpolation examples](/language/toitversus#string-interpolation) +for an expandable collection covering boundaries, expressions, and formatting. + +## Blocks are not arrow functions + +The `:` in `values.do: |value|` introduces a block. Like a callback passed to +`forEach`, it runs for each element, but its control flow is different: +`return` leaves the enclosing function, not just the iteration. + +The last expression gives a block its value. Use `continue.do` to skip to the +next iteration. Use `::` for a lambda that can be stored or returned. A lambda +uses its final expression as its result and cannot contain an explicit `return`. +This distinction lets library iteration behave like a loop while supporting +callbacks with a longer lifetime. + + + +```toit +find-long-name names: + names.do: |name| + if name.size > 4: + return name + return null + +main: + print (find-long-name ["Ada", "Grace", "Linus"]) +``` + +This prints `Grace`. A `return` inside a JavaScript `forEach` callback would +only return from that callback. Here it ends `find-long-name`. + +See [blocks and lambdas](/language/blocks-and-lambdas) for parameters and lifetime +rules. + +## Waiting does not require async functions + +In JavaScript, [`async` functions return promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), +and `await` suspends their execution until a promise settles. In Toit, ordinary +functions can wait. The task keeps its call stack while another task runs; +callers do not need an `async` marker or an `await` expression. + +Start a concurrent activity with `task::`. Within one program, tasks share +objects and run cooperatively. They do not run in parallel. A busy loop without +a yielding operation prevents other tasks from progressing. + + + +```toit +announce-later message/string -> none: + sleep --ms=10 + print message + +main: + task:: announce-later "Background done" + print "Main continues" +``` + +`announce-later` waits without any special function declaration. The main task +can continue while the background task is asleep. + +The tradeoff is that yield points are not marked at each call site. A library +call that waits can allow another task to modify shared objects. Check APIs and +use [synchronization](/language/tasks#synchronizing-between-tasks-with-monitors) +when an operation must remain consistent across waits. JavaScript's async/await +also supports sequential-looking loops; the difference here is how waiting +propagates through ordinary calls. + +## Zero and empty strings are truthy + +JavaScript treats zero and the empty string as falsy; see +[MDN's truthiness rules](https://developer.mozilla.org/en-US/docs/Glossary/Truthy). +Toit treats only `false` and `null` as falsy. There is no separate `undefined` +value. `and`, `or`, and `not` replace `&&`, `||`, and `!`. + +This affects defaults: `value or fallback` preserves `0` and `""`, but replaces +`false`. Check `value == null` when a boolean `false` must also be preserved. +Check `.size == 0` when you mean an empty collection or string. + + + +```toit +main: + print (0 or 10) + print (("" or "fallback").size) + print (null or "fallback") +``` + +Expected output: + +```text +0 +0 +fallback +``` + +## Numbers distinguish integers from floats + +Toit has signed 64-bit `int` values and floating-point `float` values. +`5 / 2` is `2`, and `5 / 2.0` is `2.5`. When translating an average or a ratio +from JavaScript, ensure a floating-point operand is present if you need the +fraction. Convert text explicitly with `int.parse` or `float.parse` and use +string interpolation to format a value as text. + + + +```toit +main: + total := 5 + count := 2 + print (total / count) + print (total.to-float / count) + print (int.parse "42") +``` + +Expected output: + +```text +2 +2.5 +42 +``` + +See [numbers](/language/math) and [conversions](/language/typeconversion). + +## Choose a class or a map + +Toit objects have fields declared in their classes. You cannot build their +shape by assigning arbitrary new properties. Use a class for a known structure +and a map for dynamic keys. `{"name": "Ada"}` is a map; access it as +`person["name"]`, not `person.name`. `{:}` is an empty map; `{}` is an empty set. + +Create an instance by calling its class, without `new`. A constructor parameter +such as `.name` initializes the corresponding field. Within methods, `this` +refers to the receiver; wrapping a call in a lambda is the way to defer it. + + + +```toit +class Person: + name/string + + constructor .name: + + greet -> none: + print "Hello $name" + +main: + person := Person "Ada" + greet-later := :: person.greet + greet-later.call + labels := {"name": person.name} + print labels["name"] +``` + +This prints `Hello Ada`, followed by `Ada`. + +TypeScript users should also note that Toit annotations such as `/string` are +checked at runtime. They are not erased type declarations. A nullable string +is `string?`. See [types](/language/definitions#type) and +[classes](/language/objects-constructors-inheritance-interfaces). + +## Catching errors returns a value + +`catch:` runs a block and returns the thrown value or `null` if nothing was +thrown. Use `try:` with `finally:` for cleanup. This is different syntax from +JavaScript's `try`/`catch`/`finally` statement. + + + +```toit +main: + error := catch: + print (int.parse "not a number") + if error: + print "Please enter an integer" +``` + +This prints `Please enter an integer`. + +Exceptions are values; strings are common. For your own error protocols, throw +a truthy value so the usual `if error` check detects it. Read +[exception handling](/language/exceptions) for cleanup and error propagation. + +## Modules and packages are resolved before running + +Each file is a library. `import .helpers` imports a neighboring `helpers.toit`; +SDK imports use names such as `import math`. Package dependencies are declared +in `package.yaml` and resolved versions live in `package.lock`. + +JavaScript packages cannot be imported as Toit libraries. Continue with +[imports](/language/imports) and the [package quick start](/language/package/pkgguide) +to structure a project and add Toit dependencies. diff --git a/docs/language/from-python.mdx b/docs/language/from-python.mdx new file mode 100644 index 00000000..6b0d472c --- /dev/null +++ b/docs/language/from-python.mdx @@ -0,0 +1,120 @@ +# Toit for Python programmers + +For readers comfortable writing Python functions and classes. Toit's indentation +will look familiar, but calls, control flow, and values have different rules. +This page highlights those differences. For the full language introduction, +read [Toit for programmers](/language/toitversus), which explains each feature +directly and draws comparisons with C/C++, JavaScript, Python, and Java/Kotlin. + +## Indentation is familiar; calls are different + +Toit uses two spaces per level, `//` for comments, and hyphens in names. +Define `greet name:` and call it as `greet "Ada"`. A zero-argument call is just +`greet`. Parentheses group expressions instead of enclosing every argument list. +Use `:=` to declare a variable and `=` to assign it again. + +Fields and zero-argument methods use the same access syntax: `name.trim` calls +a method. Wrap a call in `::` to defer it; invoke that lambda with `.call`. + +```toit +greet name/string --greeting/string="Hello" -> none: + print "$greeting $name" + +main: + greet "Ada" + greet "Grace" --greeting="Welcome" +``` + +This prints `Hello Ada` and `Welcome Grace`. + +## Test emptiness explicitly + +Only `false` and `null` are falsy in Toit. Zero, empty strings, and empty +collections are truthy. Port Python's `if items:` as `if items.size != 0:` +when the intent is to check for elements. `null` represents an absent value, +like Python's `None`. + +Toit has signed 64-bit integers, rather than Python's arbitrary-precision +integers. Integer `/` truncates toward zero: `-5 / 2` is `-2`, whereas Python's +floor division `-5 // 2` is `-3`. Use a float operand for a fractional result. + + + +```toit +describe items: + if items: + print "The list exists" + if items.size == 0: + print "The list is empty" + +main: + describe [] + print (-5 / 2) + print (5 / 2.0) +``` + +Expected output: + +```text +The list exists +The list is empty +-2 +2.5 +``` + +## Iteration blocks can return from their function + +Use `items.do: |item|` to iterate, and `items.map: |item|` to transform a list. +The block's last expression is its result. A `return` inside a block leaves the +surrounding function, making it useful for searches and early exits. +A block cannot be stored for later execution; use a `::` lambda for that. + + + +```toit +first-long words: + words.do: |word| + if word.size > 4: + return word + return null + +main: + print (first-long ["cat", "horse", "rabbit"]) +``` + +This prints `horse`. Use `continue.do` to skip an iteration without returning +from the function. Read [blocks and lambdas](/language/blocks-and-lambdas). + +## Declare fields and use runtime type contracts + +Toit classes declare their fields; methods do not add attributes dynamically. +Use maps for dynamic keys. `{:}` is an empty map and `{}` is an empty set. +Methods do not declare a `self` parameter, and can refer to fields directly. +`constructor .name:` stores the argument in the declared field. + +Annotations use `/`, as in `name/string`; they are enforced at runtime, unlike +ordinary Python type hints. `string?` also accepts `null`. +See [classes](/language/objects-constructors-inheritance-interfaces) and +[types](/language/definitions#type). + +## Ordinary functions can wait + +Toit tasks retain their call stacks while waiting for I/O. You do not need an +`async def`/`await` chain to suspend an activity. `task::` starts a task; +`sleep --ms=100` suspends the current one. Within a program, tasks share objects +and cooperate, rather than executing as parallel threads. + +A loop without yielding prevents other tasks from running. A waiting library +call can let shared state change before it returns. Read [tasks](/language/tasks) +for synchronization and examples. + +## Exceptions and imports have their own syntax + +Use `throw` to raise an exception, `catch:` to capture one, and `try:` with +`finally:` for cleanup. Each file is a library; `import .helpers` imports a sibling +file. Continue with [exceptions](/language/exceptions), [imports](/language/imports), +and the [package quick start](/language/package/pkgguide). + +For additional examples of call grouping, string interpolation, defaults, and +block control flow, expand the example collections in +[Toit for programmers](/language/toitversus). diff --git a/docs/language/imports.mdx b/docs/language/imports.mdx index c85b3001..faa153de 100644 --- a/docs/language/imports.mdx +++ b/docs/language/imports.mdx @@ -1,5 +1,9 @@ # Imports +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + Import statements make code from other libraries available in the current library. They must be at the top-level and at the top of the file (possibly following some comments). diff --git a/docs/language/index.mdx b/docs/language/index.mdx index abe03ebd..15c93645 100644 --- a/docs/language/index.mdx +++ b/docs/language/index.mdx @@ -1,896 +1,34 @@ -# Language basics +# Learn Toit -This quick-start guide is inspired by [Ruby in Twenty -Minutes](https://www.ruby-lang.org/en/documentation/quickstart/). It makes the -assumption that you have [Jaguar installed](../../getstarted/device) -on your machine. +Toit is an object-oriented, garbage-collected language designed for +microcontrollers. You can also run programs on your computer to learn the +language without connecting hardware. -Toit is an [open source](https://github.com/toitlang/toit), [object-oriented](./objects-constructors-inheritance-interfaces) -programming language for the Internet of Things. The Toit language has the following desirable properties: +## Choose your starting point -- Modern, simple, and approachable -- High-level and object-oriented -- Declarative and statically analyzable -- Safe and garbage collected +| Your experience | Start here | What you will learn | +| --- | --- | --- | +| Already comfortable programming | [Toit for programmers](/language/toitversus) | A complete language tour, with comparisons to C/C++, JavaScript, Python, and Java/Kotlin. | +| New to programming | [Beginner lessons](/language/beginner) | Build a temperature report step by step, then add lists, functions, and objects. | -Now, let's get started with some programming! +The programmer's tour explains Toit directly; you do not need to know every +language used for comparison. Familiar features are covered briefly, while +Toit-specific concepts receive more explanation and examples. -## Hello, World +If you prefer to start with changes from one language, the guides for +[JavaScript](/language/from-javascript), [Python](/language/from-python), and +[C/C++](/language/from-cpp) highlight common assumptions to revisit. They link +back to the shared tour for the full introduction. -The Toit SDK and Jaguar CLI both support running small programs directly from the command line. -If you put the following code in a file called `hello.toit`: +## Look up a language feature -``` -main: - print "Hello World!" -``` +The [language reference](/language/reference) is organized by topic. It assumes +you can already read a small Toit program; it is not a sequence of lessons. +Use it for syntax rules, edge cases, and more detailed examples. -you can run it from the command line like this: +## Use Toit in a project - - - -```txt -$ jag run -d host hello.toit -Hello World! -``` - -What just happened? The `jag` command line tool read your source code -(`hello.toit`) and started running it from the `main` method that you defined. -The `main` method consists of all the indented statements just below the method -declaration line `main:`. Toit is indentation-based like Python, so the spaces -you add to your programs are significant. - - - -```txt -$ toit hello.toit -Hello World! -``` - -What just happened? The `toit` command line tool read your source code -(`hello.toit`) and started running it from the `main` method that you defined. -The `main` method consists of all the indented statements just below the method -declaration line `main:`. Toit is indentation-based like Python, so the spaces -you add to your programs are significant. - - - -Once the program ran, it printed `Hello World!` in your terminal. This is -because the only statement in `hello.toit` is a method call, where you invoke -the `print` method with a single argument, which is the [string](./strings) to -be printed (in this case to the terminal). If you wanted to output more than -one line from your program, you could update it to: - -``` -main: - print "Hello World!" - print "Hello World!" -``` - -When you run the updated program, you will see two lines of output: - - - - -```txt -$ jag run -d host hello.toit -Hello World! -Hello World! -``` - - - -```txt -$ toit hello.toit -Hello World! -Hello World! -``` - - - -## Defining a function - -What if you want to say "Hello" a lot without getting your fingers all tired? You should define another function: - -``` -hi: - print "Hello World!" -``` - -and call that from `main`: - -``` -main: - hi - hi -``` - -Calling a function in Toit is as simple as mentioning its name. If the function -doesn't take arguments that's all you need. - -What if we want to say hello to one person, and not the whole world? Just redefine hi to take a name as an argument. - -``` -hi name: - print "Hello $name!" -``` - -This way, `hi` is a function that takes a single argument. We can use that from `main`: - -``` -main: - hi "Lars" - hi "Kasper" -``` - -and it works! - - - - -```txt -$ jag run -d host hello.toit -Hello Lars! -Hello Kasper! -``` - - - -```txt -$ toit hello.toit -Hello Lars! -Hello Kasper! -``` - - - -## Inserting strings in strings - -What's the `$name` bit? That's Toit's way of inserting something into a string. -It is called [_string interpolation_](./strings#string-interpolation). -The bit after the `$` is turned into a string (if it isn't one already) and -then substituted into the outer string at that point. You can also use this to -make sure that someone's name is properly trimmed so leading and trailing -whitespace is ignored: - - -``` -hi name="World": - print "Hello, $name.trim!" -``` - -This way, we call the `trim` function on the `name` string before we insert it -into the outer string. If we call `hi " Lars "` we still get the familiar -greeting `Hello Lars!` and not `Hello Lars !`. You can add parentheses -around the `name.trim` expression in the string to make it clearer which parts -belong to the outer string: - -``` - print "Hello, $(name.trim)!" -``` - -Maybe you already spotted that we went ahead and added one other trick to the -code above? We added a default value for the `name` parameter, so if the name -isn't supplied when you call `hi`, we use the default name "World". Now we can -try: - -``` -main: - hi - hi "Kasper" -``` - -and get the following output: - - - - -```txt -$ jag run -d host hello.toit -Hello World! -Hello Kasper! -``` - - - -```txt -$ toit hello.toit -Hello World! -Hello Kasper! -``` - - - -## Evolving into a greeter - -What if we want a real greeter around, one that remembers your name and -welcomes you and treats you with respect. You might want to use an object for -that. Let's create a `Greeter` class: - -``` -class Greeter: - name := null - - constructor .name="World": - - say-hi: print "Hi $name.trim!" - - say-bye: print "Bye $name.trim, come back soon." -``` - -The new keyword here is `class`. This defines a new class called `Greeter` and -a bunch of methods for that class. Methods are just functions that are attached -to an object. Pay special attention to the method `constructor`. There is -nothing after the `:` and the `constructor` method isn't followed by any -indented lines, so the constructor has no statements in it: - - - -``` - constructor .name="World": -``` - -This is a -[constructor](./objects-constructors-inheritance-interfaces#constructors) -and it defines how you can construct objects from the class. It says the class -`Greeter` takes a single argument (`name`), but the `.` prefix to the `.name` -parameter actually tells us that the name is immediately stored as a field on -`Greeter` objects. The field is defined just above the constructor with the -`:=` syntax. - -The field parameter `.name` still has a default value, so if we don't pass a name, the `Greeter` will greet the world. - -The `say-hi` and `say-bye` methods are introduced on the next two lines. The -methods both have a single statement in them, so we can keep them on one line -each. The `say-hi` and `say-bye` method both use the `name` field from the -object they are called on. You can refer to fields in the class of a method -simply by mentioning them (`name`). - -## Creating a greeter object - -Now let's create a greeter object and use it: - -``` -main: - greeter := Greeter " Helena " - greeter.say-hi - greeter.say-bye -``` - -We create an object simply by mentioning the constructor, `Greeter`. The -greeter object remembers the name and uses it for the two separate greetings. -If we run this, we get the following output: - - - - -```txt -$ jag run -d host hello.toit -Hi Helena! -Bye Helena, come back soon. -``` - - - -```txt -$ toit hello.toit -Hi Helena! -Bye Helena, come back soon. -``` - - - -If you want to get the name from a greeter, you can ask a greeter by calling the `name` method on it: - -``` -main: - greeter := Greeter " Helena " - print "How are you $(greeter.name)?" -``` - -This would show `How are you Helena ?`. Almost neat, right? Unfortunately, the -name isn't trimmed like we expected. Let's fix that! - -## Fields and methods - -As you have just seen, a field on an object introduces a method with the same -name. If you wanted to hide a field from the outside world, you could make it -private. By convention, methods and fields that end with an underscore (`_`) -are private and not supposed to be touched from the outside: - - - -``` -class Greeter: - name_ := null - constructor .name_="World": -``` - -This removes the `name` method from greeters, but if we really want to allow -accessing the name from the outside, we could reintroduce a getter with the -same meaning as before. - - - -``` -class Greeter: - name_ := null - - constructor .name_="World": - - name: return name_ - say-hi: print "Hi $name_.trim!" - say-bye: print "Bye $name_.trim, come back soon." -``` - -Here we use the new keyword `return` to specify the value a method returns. We -could make it slightly more interesting and trim it in the process: - - - -``` - name: return name_.trim -``` - -In this way, access to the name from the outside also gets the trimming and we -can avoid having to manually call `trim` when getting the name: - - - -``` -class Greeter: - name_ := null - - constructor .name_="World": - - name: return name_.trim - say-hi: print "Hi $name!" - say-bye: print "Bye $name, come back soon." -``` - -We can check that it works by running: - -``` -main: - greeter := Greeter " Erik " - print "How are you $(greeter.name)?" -``` - -and you should see `How are you Erik?`. `greeter` is a local variable, only visible in the -`main` method. We declared it with the `:=` syntax, just like we used `:=` to declare -member variables in classes. - -## Greetings everyone! - -This greeter isn't all that interesting though, it can only deal with one -person at a time. What if we had some kind of MegaGreeter that could either -greet the world, one person, or a whole list of people? Let's try to build -that. We will start with a class definition: - -``` -class MegaGreeter: - names := [] - - constructor name="World": - names.add name -``` - -So MegaGreeter objects have a [list](./listsetmap) of `names`. The `names` -field is initialized to the empty list (`[]`). The body of the `MegaGreeter` -constructor adds the given `name` argument to the end of the list of names. -Notice that this is different than using a `.name` parameter that automatically -assigns to the field called `name`. Mega greeters don't have a single name and -no `name` field, so here the `name` is just an ordinary parameter that we can -use in the body of the constructor. All in all, this code: - -``` -main: - greeter := MegaGreeter - print "The names are $greeter.names" -``` - -will lead to this output: - - - - -```txt -$ jag run -d host hello.toit -The names are [World] -``` - - - -```txt -$ toit hello.toit -The names are [World] -``` - - - -We can now go ahead and add greeter methods that show all the names: - - - -``` -// Greeter that says hi to everybody. -class MegaGreeter: - names := [] - - constructor name="World": - names.add name - - say-hi: - // Greet everyone individually! - names.do: print "Hello $it!" - say-bye: - everyone := names.join ", " - print "Bye $everyone, come back soon." - -main: - greeter := MegaGreeter - greeter.say-hi - greeter.say-bye - - greeter.names.add "Lars" - greeter.names.add "Kasper" - greeter.names.add "Rikke" - greeter.say-hi - greeter.say-bye -``` - -If you run this, you'll get this output: - - - - -```txt -$ jag run -d host hello.toit -Hello World! -Bye World, come back soon. -Hello World! -Hello Lars! -Hello Kasper! -Hello Rikke! -Bye World, Lars, Kasper, Rikke, come back soon. -``` - - - -```txt -$ toit hello.toit -Hello World! -Bye World, come back soon. -Hello World! -Hello Lars! -Hello Kasper! -Hello Rikke! -Bye World, Lars, Kasper, Rikke, come back soon. -``` - - - -Let's dive into the new constructs in the next sections. - -## Comments and indentation - -Not everything in your source files is meant to be run by the Toit compiler. -Sometimes, it is nice just to add comments that explain interesting things -related to your code. In the example in the last section, there were a few -single line comments: - - - -``` -// Greeter that says hi to everybody. -class MegaGreeter: -``` - -Such comments start with `//` and tell the system to ignore the rest of the line. - -You have already seen the use of indentation to give hierarchical structure to -your code. The general structure is that after a `:` you can have a single -construct if it fits on one line: - -``` -class SimpleGreeter: - - say-hi: print "Hi!" // Method all on one line. -``` - -or you can add a newline after the `:` and let the following lines that are -indented relative to the outer construct be a sequence of inner constructs: - -``` - names := [] - - // Method delimited by indentation - say-bye: - everyone := names.join ", " - print "Bye $everyone, come back soon." -``` - -For methods, we often refer to the inner constructs as the statements of a -method or the body of a method. The preferred indentation for inner constructs -is two spaces. - -For a class, everything that is indented under the class declaration line -belongs to the class. We call such things class members: - - - -``` -class MegaGreeter: - // class members start - // ... - // class members end -``` - -For methods in a class, the statements in them are nested one level further (two spaces) than the class members: - - - -``` -class MegaGreeter: - // class members start - // ... - say-hi: - // method body start - // ... - // method body end - // ... - // class members end -``` - -It is common to refer to such nested structure as _block structure_. - -## Iterating over lists - -Let's return to the `MegaGreeter` example and take a look at another place -where constructs are block structured. In the `say-hi` method, we want to call -`print` for every single name in the `names` [list](./listsetmap). We can do -this by calling `names.do` and provide the list of statements we want to run -for each element using block structure: - - - - - -``` - say-hi: - // Greet everyone individually! - names.do: print "Hello $it!" -``` - -Here the statement is on a single line, so there is no need to use indentation. -When using `names.do`, a method available on all -[collections](./listsetmap), the [special variable -`it`](./blocks-and-lambdas#block-arguments) contains the individual elements from the list in -turn. If there are 5 elements in the `names` list, we will call `print` 5 times -producing 5 separate lines of output. - -You can play with the methods on list by modifying and running the sample below: - -``` -main: - list := [ "Horse", "Fish", "Radish", "Baboon" ] - print "There are $(list.size) elements in the list" - print "Here they are:" - list.do: print "Element = $it" - - print "Here they are (sorted):" - list.sort --in-place - list.do: print "Element = $it" -``` - -One of the methods on lists that is very useful when constructing strings is -`join`. It produces a string from a list of strings by joining the parts and -adding a separator between them. We use this in the `MegaGreeter` example to -produce a single comma-separated list of names for the single line output of -`say-bye`: - - - - - -``` - say-bye: - everyone := names.join ", " - print "Bye $everyone, come back soon." -``` - -## Named arguments - -Perhaps you don't always want to say "Hello", so you add an argument with a default value to the `say-hi` method: - -``` - say-hi greeting="Hello,": - // Greet everyone individually! - names.do: print "$greeting $it!" -``` - -Now the user of your class can write: - -``` -main: - greeter := MegaGreeter - greeter.names.add "Lars" - greeter.names.add "Kasper" - greeter.say-hi "Kaixo," -``` - -Which produces the output: - -```txt -Kaixo, World! -Kaixo, Lars! -Kaixo, Kasper! -``` - -However, at the calling site it may not be clear what the argument "Kaixo" is -for. We can make it clearer with a named argument: - - - - - -``` - say-hi --greeting="Hello,": - // Greet everyone individually! - names.do: print "$greeting $it!" -``` - -Now we can use this with: - -``` -main: - greeter := MegaGreeter - greeter.say-hi - greeter.names.add "Lars" - greeter.names.add "Kasper" - greeter.say-hi --greeting="Hej," -``` - -which outputs: - -```txt -Hello, World! -Hej, World! -Hej, Lars! -Hej, Kasper! -``` - -## If statements and basic expressions - -We can program a ridiculously inefficient Fibonacci sequence generator using `if` and recursion: - -``` -fib n: - if n <= 1: return n - return (fib n - 1) + (fib n - 2) - -main: - print "The 10th Fibonacci number is $(fib 10)" -``` - -This defines a top-level function called `fib` that is not a member of any -class. (We already saw `main`, which is a top level function with a special -name.) - -The `fib` function is recursive, calling itself, and also makes use of a few -new features. The [if-statement](./loops#if-statements) is well known from -other languages. In Toit it works by taking an expression and conditionally -evaluating a block. Like other blocks we could have used indentation to group -multiple lines. - -Toit also has the usual array of infix operators, `+`, `-`, `*`, `/`, `%` etc. -and the relational operators `<`, `<=`, `>`, `>=`, `==` and `!=`. The -operators have higher [precedence](./syntax#precedence) than function -arguments, so we had to group the calls in parentheses to get the desired -behavior. The high precedence is what makes the arguments for the recursive -invocation of `fib` work. - -## Loops - -This is a terribly slow way to calculate a Fibonacci number though, and we -could do it with a simple [loop](./loops#loops): - -``` -fib2 n: - s1 := 0 - s2 := 1 - n.repeat: - s3 := s1 + s2 - s1 = s2 - s2 = s3 - return s1 -``` - -Here we are using the `repeat` method on numbers, which runs a block a given -number of times. Like for the `do` method, there's an automatic variable, `it` -that gives the iteration number: - -``` -// Prints the numbers from 0 to n (exclusive). -print-n-numbers n: - n.repeat: print it -``` - -The `repeat` method is simple and efficient, but sometimes we need something -more flexible, and for that we have the well-known `while` and `for` -statements: - -``` -// Prints the odd numbers less than n. -print-odd-numbers n: - for i := 1; i < n; i += 2: - print i - -// Returns if the Collatz conjecture is true. -collatz n: - while n > 1: - if n % 2 == 0: n = n / 2 - else: n = n * 3 + 1 -``` - -## Maps and sets - -Perhaps we need titles for our greeters: - - - -``` -class MegaGreeter: - names := [] - titles := {:} - - constructor: - - add name title: - names.add name - titles[name] = title - say-hi: - // Greet everyone individually! - names.do: print "Hello, $titles[it] $it!" - -main: - greeter := MegaGreeter - greeter.add "Lars" "Mr." - greeter.add "Rikke" "Dr." - greeter.add "Günter" "Herr Professor Doktor Doktor" - greeter.say-hi -``` - -Here we use a [hash map](./listsetmap) to store the appropriate title for each -name. The empty map is given by `{:}` and we use `[]` to access the values for -each key. The empty set is `{}` and we already met the empty list, `[]`. The -lookup syntax `[]` also works on lists, so instead of the 'do' method we could -have used: - - - - - -``` - say-hi: - for i := 0; i < names.size; i++: - print "Hello, $names[i]" -``` - -## Blocks and lambdas - -We already saw the `repeat` method on integers and the `do` method on lists: - -``` -main: - // Print the numbers from 1 to 10, one per line. - 10.repeat: - print it + 1 - my-list := [1, 2, 3] - // Print the elements in my-list, one per line. - my-list.do: - print it -``` - -Syntactically they look like they are built in to the language like `if` and -`for`, but they are actually normal methods on the List and int classes: - - - -``` -class List: - // ... - do [block]: - size.repeat: block.call this[it] - -class int: - // ... - repeat [block]: - for i := 0; i < this; i++: - block.call i -``` - -They are making use of a feature called [blocks](./blocks-and-lambdas). These are -snippets of code that can be passed down the stack as arguments to methods and -functions. At the call site we precede the block with a colon, '`:`', and at -the function definition we surround the parameter name with square brackets, -'`[]`'. Often, there is one block parameter, it is in the final position and -it is called `block`. - -If a callback needs to survive the scope in which it is defined, we can't use -blocks. Instead, we can use [lambdas](./blocks-and-lambdas#lambdas): - -``` -// Returns a function that adds n to its argument. -add-n n -> Lambda: - return (:: it + n) -``` - -The syntax for lambdas is the same as for blocks, except that the we use `::` -instead of `:`. The lambda above is a function that takes a single argument and -returns the sum of that argument and `n`. We can use it like this: - -``` -main: - add-5 := add-n 5 - print (add-5.call 10) -``` - -This will print `15`. - -## Blocks and lambdas with multiple arguments - -Blocks and lambdas can have parameters, just like methods. The parameters are -listed after the `::` or `:`. Here is an example of a block with two parameters: - -``` -main: - map := { "Lars": 1, "Kasper": 2 } - map.do: | key value | - print "$key has value $value" -``` - -The block in the `do` method has two parameters, `key` and `value`. The `do` -method will call the block with each key-value pair in the map. - -## Blocks and lambdas that return a value - -A block or lambda can return a value each time it is run. This is used for example -in the `filter` method on lists. - -``` -// Takes a list of words, and returns a new list with only the -// words that are 5 characters or fewer. -short-words words: - return words.filter: - it.size <= 5 -``` - -The `filter` method calls the block, `it.size <= 5` for each element in the -original list, and returns a new list containing only the short words. - -Note that there is no `return` statement in the block. A block will return the -value of the last statement to the place where it was invoked with -`block.call`. In this case there is only one statement, which is the -[boolean](./booleans) expression `it.size <= 5`. - -If you use the `return` keyword in a block then it returns from the syntactic -function or method in which it is written. Usually this will behave as you -would expect: - -``` -wheres-walter list: - list.do: - if it.starts-with "Walter ": - return it - return null - -main: - print (wheres-walter ["Ib Michael", "Walter White", "Marie Curie"]) -``` - -The `return` keyword is inside a block that is passed to the `do` method. When -the name that starts with `"Walter "` is found we immediately return the full -name from the `wheres-walter` function without continuing to iterate over the -list. +After learning the language, use the [package quick start](/language/package/pkgguide) +to add dependencies, or [run on your device](/getstarted/device) to connect an +ESP32. The [hardware and networking tutorials](/tutorials) assume basic Toit +knowledge and list their additional setup requirements. diff --git a/docs/language/listsetmap.mdx b/docs/language/listsetmap.mdx index 3077a103..ba68ad98 100644 --- a/docs/language/listsetmap.mdx +++ b/docs/language/listsetmap.mdx @@ -1,5 +1,9 @@ # Lists, byte arrays, sets and maps +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + The following classes are included in the core SDK as part of the [collections.toit module](https://libs.toit.io/core/collections/library-summary): diff --git a/docs/language/loops.mdx b/docs/language/loops.mdx index fcef2658..a7ad25c9 100644 --- a/docs/language/loops.mdx +++ b/docs/language/loops.mdx @@ -1,5 +1,9 @@ # Control flow +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + ## If-statements If-statements work in a very similar way to other languages. diff --git a/docs/language/math.mdx b/docs/language/math.mdx index 2ae6cc1a..8f73ea2c 100644 --- a/docs/language/math.mdx +++ b/docs/language/math.mdx @@ -1,5 +1,9 @@ # Mathematics +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + Find mathematical algorithms in the [math module](https://libs.toit.io/math/library-summary), and the numbers available in the [numbers module](https://libs.toit.io/core/numbers/library-summary) of diff --git a/docs/language/objects-constructors-inheritance-interfaces.mdx b/docs/language/objects-constructors-inheritance-interfaces.mdx index be2753aa..78055099 100644 --- a/docs/language/objects-constructors-inheritance-interfaces.mdx +++ b/docs/language/objects-constructors-inheritance-interfaces.mdx @@ -1,5 +1,9 @@ # Classes +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + In Toit, everything is an object, including things that may be non-object "primitive types" in other languages. For example, an [integer](../math) is an object, and has methods like `abs`: diff --git a/docs/language/reference.mdx b/docs/language/reference.mdx new file mode 100644 index 00000000..6776a9a3 --- /dev/null +++ b/docs/language/reference.mdx @@ -0,0 +1,16 @@ +# Language reference + +For readers who can already follow a small Toit program. These pages explain +individual features and their edge cases; read them as needed. For a first +program, start with the [beginner tutorial](/language/beginner). For a quick +orientation from another language, read [Toit for programmers](/language/toitversus). + +| Topic | Pages | +| --- | --- | +| Reading and organizing code | [Syntax](/language/syntax), [definitions and types](/language/definitions), [imports](/language/imports), [style](/language/style) | +| Values and data | [Booleans](/language/booleans), [numbers](/language/math), [strings](/language/strings), [collections and bytes](/language/listsetmap), [conversions](/language/typeconversion), [bitwise operations](/language/bitmask) | +| Program structure | [Control flow](/language/loops), [classes and interfaces](/language/objects-constructors-inheritance-interfaces), [blocks and lambdas](/language/blocks-and-lambdas) | +| Errors and concurrency | [Exceptions](/language/exceptions), [tasks and synchronization](/language/tasks) | + +The [SDK documentation](/language/sdk) covers libraries supplied with Toit. +[Packages](/language/package) explains how to use libraries distributed separately. diff --git a/docs/language/strings.mdx b/docs/language/strings.mdx index 74cfdaa9..e43b8a4a 100644 --- a/docs/language/strings.mdx +++ b/docs/language/strings.mdx @@ -1,5 +1,9 @@ # Strings +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + Toit strings are immutable value objects, containing Unicode strings. The characters are encoded with UTF-8, although most of the time you can ignore this. @@ -353,9 +357,9 @@ main: ``` The variable after the dollar sign is turned into a string -by calling its `stringify` method. The variable stops at -the first non-alpha-numeric character, but the expression -can be extended using dot or `[]` notation: +by calling its `stringify` method. Interpolation follows Toit identifier rules, +including hyphens and underscores, and can include member access and indexing +using dot or `[]` notation: ``` main: @@ -367,10 +371,10 @@ main: print "Level $list[1], the band." // >> Level 42, the band. ``` -If the variable is followed immediately by alphanumeric -characters or we want a more complicated interpolation -expression we can put parentheses, `()`, around an arbitrary -expression: +To separate interpolation from literal text that could be read as part of the +expression, or to interpolate a more complicated expression, put parentheses +around the expression. For example, `"$(name)-sensor"` keeps the hyphen and +`sensor` outside the interpolated name: ``` main: diff --git a/docs/language/style.mdx b/docs/language/style.mdx index 21ddec47..6ae81233 100644 --- a/docs/language/style.mdx +++ b/docs/language/style.mdx @@ -1,5 +1,9 @@ # Style guide +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + This document describes the preferred style for the Toit language. To learn about Toit's syntax see [the syntax summary](../syntax). To learn about Toit's diff --git a/docs/language/syntax.mdx b/docs/language/syntax.mdx index 767a78cb..31bd32e5 100644 --- a/docs/language/syntax.mdx +++ b/docs/language/syntax.mdx @@ -1,5 +1,9 @@ # Syntax fundamentals +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + This document describes the basics of the Toit language. To learn about Toit's documentation convention, see the language [documentation convention](../sdk/toitdoc) section. diff --git a/docs/language/tasks.mdx b/docs/language/tasks.mdx index d7ba79d8..22c340c6 100644 --- a/docs/language/tasks.mdx +++ b/docs/language/tasks.mdx @@ -1,16 +1,18 @@ # Tasks +This guide assumes you can read Toit functions, loops, and lambdas. It explains +cooperative scheduling and then covers synchronization primitives. For an +orientation from another language, read [Toit for programmers](/language/toitversus). + ## Introduction -Some languages, like Java or C#, have multiple threads running at the same -time. These are separate control flows that can manipulate each other's data. -Because there can be different threads using the same objects, it is easy to -make programming mistakes called race conditions. +Concurrent activities need independent control flow. JavaScript's async/await, +for example, lets code wait for promises while other work proceeds. Toit uses +tasks with independent call stacks: ordinary functions can wait without an +`async` declaration or an `await` expression at each call site. -Other languages, like JavaScript, have only one thread. This is very limiting -in that your program constantly has to return to an _event loop_, which makes -it much harder to program. Your code must be written in a _non-blocking_ style -with no long-running loops. +Tasks within a Toit program share objects and run cooperatively. This simplifies +updates that do not yield, but shared state can still change while a task waits. ## Tasks @@ -58,11 +60,9 @@ In this example, we can see that tasks or threads simplify the programming considerably. One LED is flashing once per second, while the other is flashing every 246ms. To code this in one loop, we would have to write a program that switched the LEDs at the times 0, 123, 246, 369, 492, 500, 615ms etc. -Alternatively to code this in an event-driven language like JavaScript, we -would have to remove all the loops and instead use a state machine with -scheduled callbacks to perform the LED switching and update variables that -represent state (in this case to track whether the next callback should switch -on or off). +JavaScript can express similar loops using async functions and awaited timers. +In Toit, the waiting operation suspends an ordinary call stack, so helpers and +their callers do not need async/await syntax. ## Cooperative scheduling @@ -105,7 +105,6 @@ exists. While it is calculating, it doesn't wait for anything (yield), and so no other tasks can run. As soon as the uncooperative task starts running it hogs the CPU and prevents other tasks from running, stopping 'my-starved-task' from emitting messages (if it ever did so). -task will stop appearing. The advantage of tasks in comparison with threads is that they take turns. To take an oversimplified example: diff --git a/docs/language/toitversus.mdx b/docs/language/toitversus.mdx index d7b15c48..5e54de2c 100644 --- a/docs/language/toitversus.mdx +++ b/docs/language/toitversus.mdx @@ -1,159 +1,800 @@ -# Language comparison - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ToitPython
Current object
this
self
Single line comments
//
#
Logical 'and', 'or' and 'not' operators
and or not
and or not
Shift left, right, unsigned right
<<  >>  >>>
<< >> (no unsigned shift)
Integer division
/ (on integer types)
//
Integer sizes
64
(arbitrary)
Statement grouping
(indent)
(indent)
Define a class Foo that inherits from Bar
class Foo extends Bar:
class Foo(Bar):
Define constructor for class Foo
constructor x:
def __init__(self, x):
Define constructor for class Foo that calls constructor of superclass Bar
constructor x:
  super x
def __init__(self, x):
  super(Bar, self).__init__(x)
Constructor that assigns to fields
constructor .x:
def __init__(self, x):
  self.x = x
Check object has type
bar is Foo
isinstance(bar, Foo)
Check object does not have type
bar is not Foo
not isinstance(bar, Foo)
Call a method foo with two arguments
foo x y
foo(x, y)
Declare a member variable in a class
x := null
x := ?
x/int := 0
x/int := ?
self.x = null
Declare a local variable in a method
x := null
x := ?
x/int := 0
x/int := ?
x = null
Define a constant
X ::= 0
Define a constant in a class
static X ::= 0
Define a top-level function
foo x y:
def foo(x, y):
Define an instance method in a class
foo x y:
def foo(self, x, y):
Define a static method in a class
static foo x y
If statement
if condition:
if condition:
Fixed loop
end.repeat: | i |
for i in range(end):
Three-part for loop
for i := 0; i < end; i++:
Iterate over collection
collection.do: | x |
for x in collection:
While loop
while condition:
while condition:
Import local from library
import .library
Import from library
import library
import library
Import into current namespace
import library show *
from library import *
Print/log
print "Hello"
print("Hello")
Print with interpolation
print "Hello $name"
print("Hello %s" %(name))
Interpolate with padding
print "Hello $(%9s name)"
print("Hello %9s", %(name))
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Toit
C++
Current object
this
this
Single line comments
//
//
Logical 'and', 'or' and 'not' operators
and or not
&& || ! (or and or not)
Shift left, right, unsigned right
<<  >>  >>>
<< >> >>  (on unsigned type)
Integer division
/ (on integer types)
/ (on integer type)
Integer sizes
64
8/16/32/64...
Statement grouping
(indent)
{}
Define a class Foo that inherits from Bar
class Foo extends Bar:
class Foo : public Bar {
Define constructor for class Foo
constructor x:
Foo(int x) {
Define constructor for class Foo that calls constructor of superclass Bar
constructor x:
  super x
Foo(int x) : Bar(x) {
Constructor that assigns to fields
constructor .x:
Foo(int x) : x(x) {
Check object has type
bar is Foo
(dynamic_cast<Foo*>(bar) != null)
Check object does not have type
bar is not Foo
(dynamic_cast<Foo*>(bar) == null)
Call a method foo with two arguments
foo x y
foo(x, y);
Declare a member variable in a class
x := null
x := ?
x/int := 0
x/int := ?
int x;
int x = 0;
Declare a local variable in a method
x := null
x := ?
x/int := 0
x/int := ?
int x;
int x = 0;
Define a constant
X ::= 0
const int X = 0;
Define a constant in a class
static X ::= 0
static const int X = 0;
Define a top-level function
foo x y:
int foo(int x, int y) {
Define an instance method in a class
foo x y:
int foo(int x, int y) {
Define a static method in a class
static foo x y
static int foo(int x, int y) {
If statement
if condition:
if (condition) {
Fixed loop
end.repeat:
i |                    | for (int i = 0; i < end; i++) {
Three-part expression loop
for i := 0; i < end; i++:
for (int i = 0; i < end; i++) {
Iterate over collection
collection.do:
x |                 | for (auto x : collection) {
While loop
while condition:
while (condition) {
Import local from library
import .library
#include "file"
Import from library
import library
#include <file>
Import into current namespace
import library show *
#include <file> using namespace file;
Print/log
print "Hello"
cout << "Hello\n"
Print with interpolation
print "Hello $name"
cout << "Hello " << name << "\n";
Interpolate with padding
print "Hello $(%9s name)"
printf("Hello %9s\n", name);
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Toit
Java
Current object
this
this
Single line comments
//
//
Logical 'and', 'or' and 'not' operators
and or not
&& || !
Shift left, right, unsigned right
<<  >>  >>>
<< >> >>>
Integer division
/ (on integer types)
/ (on integer type)
Integer sizes
64
8/16/32/64
Statement grouping
(indent)
{}
Define a class Foo that inherits from Bar
class Foo extends Bar:
public class Foo extends Bar {
Define constructor for class Foo
constructor x:
public Foo(int x) {
Define constructor for class Foo that calls constructor of superclass Bar
constructor x:
  super x
public Foo(int x) {
  super(x);
Constructor that assigns to fields
constructor .x:
Foo(int x) {
  field_x = x;
Check object has type
bar is Foo
bar instanceof Foo
Check object does not have type
bar is not Foo
!(bar instanceof Foo)
Call a method foo with two arguments
foo x y
foo(x, y);
Declare a member variable in a class
x := null
x := ?
x/int := 0
x/int := ?
int x;
int x = 0;
Declare a local variable in a method
x := null
x := ?
x/int := 0
x/int := ?
int x;
int x = 0;
Define a constant
X ::= 0
Define a constant in a class
static X ::= 0
public static final int X = 0;
Define a top-level function
foo x y:
Define an instance method in a class
foo x y:
public int foo(int x, int y) {
Define a static method in a class
static foo x y
public static int foo(int x, int y) {
If statement
if condition:
if (condition) {
Fixed loop
end.repeat: | i |
for (int i = 0; i < end; i++) {
Three-part expression loop
for i := 0; i < end; i++:
for (int i = 0; i < end; i++) {
Iterate over collection
collection.do: | x |
for (Foo x : collection) {
While loop
while condition:
while (condition) {
Import local from library
import .library
Import from library
import library
Import into current namespace
import library show *
import com.example.Class;
Print/log
print "Hello"
System.out.println("Hello");
Print with interpolation
print "Hello $name"
System.out.printf("Hello %s\n, name);
Interpolate with padding
print "Hello $(%9s name)"
System.out.printf("Hello %9s\n, name);
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Toit
JavaScript
Current object
this
this
Single line comments
//
//
Logical 'and', 'or' and 'not' operators
and or not
&& || !
Shift left, right, unsigned right
<<  >>  >>>
<< >> >>>
Integer division
/ (on integer types)
Math.trunc(x / y)
Integer sizes
64
32, 53
Statement grouping
(indent)
{}
Define a class Foo that inherits from Bar
class Foo extends Bar:
class Foo extends Bar {
Define constructor for class Foo
constructor x:
constructor(x) {
Define constructor for class Foo that calls constructor of superclass Bar
constructor x:
  super x
constructor(x) {
  super(x);
Constructor that assigns to fields
constructor .x:
constructor(x) {
  this.x = x
Check object has type
bar is Foo
bar instanceof Foo
Check object does not have type
bar is not Foo
!(bar instanceof Foo)
Call a method foo with two arguments
foo x y
foo(x, y)
Declare a member variable in a class
x := null
x := ?
x/int := 0
x/int := ?
this.x = null
Declare a local variable in a method
x := null
x := ?
x/int := 0
x/int := ?
var x = null
Define a constant
X ::= 0
const X = 0
Define a constant in a class
static X ::= 0
Define a top-level function
foo x y:
function foo(x, y) {
Define an instance method in a class
foo x y:
foo(x, y) {
Define a static method in a class
static foo x y
If statement
if condition:
if (condition) {
Fixed loop
end.repeat: | i |
for (var i = 0; i < end; i++) {
Three-part expression loop
for i := 0; i < end; i++:
for (var i = 0; i < end; i++) {
Iterate over collection
collection.do: | x |
for (var x in collection) {
While loop
while condition:
while (condition) {
Import local from library
import .library
Import from library
import library
var library = require('library');
Import into current namespace
import library show *
Print/log
print "Hello"
console.log("Hello")
Print with interpolation
print "Hello $name"
console.log("Hello " + name + "\n")
Interpolate with padding
print "Hello $(%9s name)"
- -
-
+# Toit for programmers + +This is a complete first tour for readers who already program in another +language. It introduces Toit's syntax, values, functions, objects, libraries, +and concurrency. Familiar concepts get a short explanation; features such as +blocks and cooperative tasks get more space. + +Read it in order, or use the table of contents to skip familiar topics. We use +C/C++, JavaScript, Python, and Java/Kotlin as points of comparison. You do not +need to know all of them: each section explains the Toit behavior directly. +Optional example collections explore variations without interrupting the main +explanation. + +You can run the complete examples on your computer using the +[local setup](/language/beginner#1-run-your-first-program); no hardware is required. +If you are new to programming, use the [beginner lessons](/language/beginner). +The language-specific guides for [JavaScript](/language/from-javascript), +[Python](/language/from-python), and [C/C++](/language/from-cpp) highlight what to +watch for when coming from those languages. + +## The differences that matter + +| Choice in Toit | Consequence for your code | +| --- | --- | +| Calls use spaces; zero-argument methods need no parentheses | `sensor.read` calls a method. Wrap a call in parentheses when passing its result to another call. | +| Optional types are enforced at runtime | An annotation is a contract, not just editor metadata. Untyped code still runs, and type errors can still occur at runtime. | +| Blocks and lambdas are distinct | Use blocks for scoped iteration and control flow; use lambdas when code must be stored for later. Their `return` behavior differs. | +| Objects have declared fields and methods | Use classes for structured objects and maps for dynamic keys. | +| Tasks have their own stacks and cooperate | Waiting code reads sequentially, but calls can yield and shared state can change while you wait. | +| Memory is garbage collected | You do not free objects; you still explicitly release resources such as open files. | + +## Read calls before translating syntax + +A program starts in `main`. Functions and classes can be declared at the top +level; there is no need for a wrapper class. Indentation groups statements, +with two spaces per level. Comments start with `//`, or use `/* ... */` for +multiple lines. Java programmers can define `main` and other functions directly +in a file, as in Kotlin. The `/int` and `-> int` annotations below specify parameter +and return types; they can be omitted. + +```toit +add a/int b/int -> int: + return a + b + +main: + reading-count := 3 + reading-count = reading-count + 1 + print (add reading-count 2) +``` + +This prints `6`. `:=` declares a mutable variable; `=` assigns to one. +`::=` declares a final binding, which does not make the referenced object +immutable. Toit uses two-space indentation and allows hyphens in names: +`reading-count` is one identifier, while `reading - count` is subtraction. + +`add reading-count 2` calls `add` with two arguments. Commas separate elements +in collection literals, but do not separate call arguments. Parentheses group +expressions: `print (add reading-count 2)` passes the result to `print`. +Arithmetic binds more tightly than calls, so `add 1 2 * 3` means `add 1 (2 * 3)`. + +A bare method name invokes it: `clock.read` is a call, not a function value. +This also lets a field getter become a computed method without changing callers. +Named arguments are explicit at both ends: `sleep --ms=100`. +See [syntax](/language/syntax) for precedence and declarations. + + + +Each numbered case illustrates a different call rule. This is a complete program. + + + +```toit +add a/int b/int -> int: + return a + b + +label value/int --prefix/string="Value" --loud/bool=false -> string: + text := "$prefix: $value" + return loud ? "$text!" : text + +main: + // 1. Arithmetic binds more tightly than the call. Prints 7. + print (add 1 2 * 3) + + // 2. Parentheses make a call's result an operand. Prints 9. + print ((add 1 2) * 3) + + // 3. Each continuation line supplies one argument. Prints 3. + print + add 1 + 2 + + // 4. Omitted named arguments use defaults. Prints Value: 3. + print (label 3) + + // 5. A bare boolean flag means true. Prints Count: 3!. + print (label 3 --prefix="Count" --loud) + + // 6. The no- prefix passes false. Prints Count: 3. + print (label 3 --prefix="Count" --no-loud) +``` + +In case 3, putting `1 2` together on one continuation line would make it one +expression, not two arguments. A continuation line can itself be a nested call. + + + +## Functions and scope + +Define parameters after the function name. Positional parameters can have +default values; named parameters have a `--` prefix in both the definition and +the call. Use `return` for a function's result. A function without a result can +be annotated `-> none`. + + + +```toit +greet name/string="World" --greeting/string="Hello" -> string: + return "$greeting $name" + +main: + print (greet) // Prints Hello World. + print (greet "Ada" --greeting="Hi") // Prints Hi Ada. +``` + +C/C++ and JavaScript programmers should notice that named arguments are part +of the call's syntax, not fields in an options object. A boolean named argument +can be passed as `--verbose` for true or `--no-verbose` for false. Toit supports +overloading by argument count and names, rather than selecting overloads by +parameter types. + +A local name belongs to its enclosing scope. A top-level variable is a global; +its initializer runs on first access. Use `::=` for a final binding and +uppercase names for constants, for example `MAX-READINGS ::= 100`. Finality +prevents reassignment, not mutation of the referenced object. These rules also +apply to fields, which we will use in the classes section. + +## String interpolation + +Double-quoted strings support `$name` and `$(expression)` interpolation: + + + +```toit +main: + name := "Ada" + print "Hello $name" // Prints Hello Ada. + print "Next reading: $(21 + 1) C" // Prints Next reading: 22 C. +``` + +Interpolation includes member access and indexing: `"$name.size"` inserts the +size, while `"$(name).size"` inserts the name followed by literal `.size`. +Hyphens can belong to identifiers, so use `"$(name)-sensor"` when you want a +literal suffix. Formatting goes inside the parentheses, as in `"$(%.2f 3.14159)"`. + +Strings are immutable UTF-8 text. Unlike Python's character indexing or +JavaScript's UTF-16 code-unit indexing, Toit's string offsets are byte offsets: +`"é".size` is `2`. Indexing at a character boundary returns its Unicode code +point; indexing at a continuation byte returns `null`. Slices must end at +character boundaries. Use `text.do --runes:` to iterate over code points instead +of treating every byte offset as a character. + +Use triple double quotes for multiline strings, `\n` for a newline, and `\$` +for a literal dollar sign. Single quotes denote a code point: `'A'` is the +integer `65`, not a one-character string. + + + +**Member access and boundaries.** Dot access can call a zero-argument method; +parentheses determine where interpolation ends. + + + +```toit +main: + name := "Ada" + padded := " Ada " + + // 1. Read a member. Prints 3. + print "$name.size" + + // 2. Keep the suffix literal. Prints Ada.size. + print "$(name).size" + + // 3. Invoke a method. Prints Ada. + print "$padded.trim" + + // 4. Follow interpolation with a hyphen. Prints Ada-sensor. + print "$(name)-sensor" +``` + +**Indexing and expressions.** Use parentheses for calls and arithmetic, and +escape the dollar sign when it should be printed literally. + + + +```toit +main: + readings := [21, 26] + count := 3 + + // 5. Index into a list. Prints First: 21. + print "First: $readings[0]" + + // 6. Evaluate arithmetic. Prints Next: 4. + print "Next: $(count + 1)" + + // 7. Evaluate a call with an argument. Prints Parsed: 42. + print "Parsed: $(int.parse "42")" + + // 8. Print a literal dollar sign. Prints $3. + print "\$$count" +``` + +**Formatting.** Each format specifier is next to its value. Delimiters make +padding visible in the last example. + + + +```toit +main: + // 9. Limit fractional digits. Prints 3.14. + print "$(%.2f 3.14159)" + + // 10. Format hexadecimal. Prints 0x2a. + print "0x$(%x 42)" + + // 11. Pad with zeros. Prints 0x0d. + print "0x$(%02x 13)" + + // 12. Pad to a decimal width. Prints > 7<. + print ">$(%4d 7)<" +``` + +See [strings](/language/strings#string-interpolation) for more formatting options +and the string representation rules. + + + +## Types are optional, but checked + +The `/int` parameters and `-> int` return annotation on `add` specify runtime +checks. An omitted annotation permits dynamic values. Analysis helps find +mistakes before execution, but does not turn Toit into a language where every +type error is rejected before running. + +Types are non-nullable by default; `string?` accepts a string or `null`. +Use annotations on function signatures and fields to document and enforce +expectations. Unlike TypeScript annotations or ordinary Python type hints, +these annotations constrain values at runtime. `any` accepts any value, +including `null`; `none` marks a function with no result. Kotlin readers will +recognize the nullable `?` suffix; C++ readers should distinguish a nullable +object reference from a pointer they can dereference or perform arithmetic on. +Local types are often inferred by the editor and need not be written out. + +Use `value is Type` to test a type, and `value as Type` for a checked cast. +The cast does not convert the value; use conversion methods such as `to-float` +or parsing functions such as `int.parse` for that. +See [types](/language/definitions#type). + +## Numbers, booleans, and operators + +Only `false` and `null` are falsy. Zero, empty strings, and empty collections +are truthy. Integers are signed 64-bit values; integer division truncates toward +zero. `5 / 2` is `2`, while `5 / 2.0` is `2.5`. These rules matter when porting +default expressions, emptiness checks, and calculations. + +Arithmetic uses `+`, `-`, `*`, `/`, and `%`; comparisons use `==`, `!=`, `<`, +`<=`, `>`, and `>=`. `and`, `or`, and `not` are the logical operators. +Unlike Java and Kotlin, a condition does not require a boolean value: the +truthiness rules above apply. `and` and `or` short-circuit and return an operand, +which makes `name or "World"` a useful default when `name` is nullable. Check `.size == 0` for emptiness. + +Integers have a fixed range, unlike Python integers. Floats are double precision. +Hexadecimal and binary literals use `0x` and `0b`; separate digits with `_`, as +in `0xffff_ffff`. Bitwise operations use `&`, `|`, `^`, `~`, `<<`, `>>`, and +`>>>`; the last is an unsigned right shift. See [numbers](/language/math) and +[bitwise operations](/language/bitmask) for the detailed rules. + + + +These expressions distinguish missing data from empty data. The helper shows +how the same expression handles both `null` and an empty list. + + + +```toit +describe value: + print (value or "fallback") + print (value and value.size) + +main: + // Null takes the fallback and skips member access. Prints fallback, null. + describe null + + // An empty list is truthy. Prints [], 0. + describe [] + + // Zero is preserved by or. Prints 0. + print (0 or 10) + + // False is replaced by or. Prints fallback. + print (false or "fallback") + + // Integer division truncates toward zero. Prints -2. + print (-5 / 2) + + // A float operand preserves the fraction. Prints -2.5. + print (-5 / 2.0) +``` + +`and` returns the last evaluated operand; it does not always return a boolean. +In `describe`, a truthy argument must support `.size`. Guarding against absence +does not check whether an object has the member you want. + + + +## Blocks are part of control flow + +Blocks let ordinary methods implement control structures. The `:` following +`values.do` passes a block, and `|value|` names its parameter. The block can +read and update variables in the surrounding function. For a short block with +one parameter, `it` is an implicit parameter name, as in `values.do: print it`. + +Unlike a JavaScript `forEach` callback or a C++ lambda, a Toit block can return +from the function in which it was written: + + + +```toit +first-positive values: + values.do: |value| + if value > 0: + return value + return null + +main: + print (first-positive [-2, 0, 7, 9]) +``` + +This prints `7`: `return` inside the block returns from `first-positive`. +A block's last expression supplies its result to its caller; it does not need +`return`. This makes library methods such as `do`, `map`, and `repeat` work +like control structures. Blocks cannot be saved in fields or returned for later +use, allowing their lifetime to stay within the enclosing call. + +### Write a method that accepts a block + +A block parameter is written in square brackets. Invoke it with `.call`, passing +its arguments in the usual way. The caller's local variables remain accessible +while the called method runs: + + + +```toit +with-offset offset/int [action]: + return action.call offset + +main: + base := 10 + result := with-offset 5: |offset| + base + offset + print result // Prints 15. +``` + +The final expression `base + offset` supplies the block's result to +`action.call`. The `return` in `with-offset` then returns that result to `main`. +These are different from putting `return` inside the block, which would leave +`main` itself. + +### Lifetime explains the low cost + +Blocks can be passed down through calls, but cannot escape into fields, +collections, globals, or return values. A lambda cannot capture a block. Those +restrictions keep the referenced locals alive for every use of the block. + +For C/C++ readers, the VM representation helps explain the design: a block +reference is a small integer encoding a position relative to the stack base. +Loading that reference pushes a value; it does not allocate a heap closure to +copy the captured locals. This is an implementation explanation, not a promise +about instruction counts. C++ lambdas can also avoid heap allocation; the key +Toit distinction is that the language enforces a separate lifetime for blocks. + +That makes blocks a practical default for scoped callbacks, including iteration +and resource-management helpers. Use the parameter form required by the API: +`[action]` expects a block, whereas a stored callback needs a lambda. + +### Lambdas can outlive the call + +A lambda starts with `::` and can outlive the call that creates it. Its final +expression supplies its result; it cannot contain an explicit `return`: + + + +```toit +make-adder amount/int -> Lambda: + return :: |value| value + amount + +main: + add-five := make-adder 5 + print (add-five.call 3) +``` + +This prints `8`. Use this form for stored callbacks. To pass a method for later +execution, wrap its call in a lambda. Read [blocks and lambdas](/language/blocks-and-lambdas) +for block parameters and local exits such as `continue.do`. + + + +**Use the last expression as a block's result.** An explicit `return` here would +leave `main` instead of supplying one mapped value. + + + +```toit +main: + doubled := [1, 2, 3].map: it * 2 + print doubled // Prints [2, 4, 6]. +``` + +**Skip an iteration with `continue.do`.** The function continues after the loop. + + + +```toit +main: + [1, 2, 3].do: |value| + if value == 2: continue.do + print value + print "Done" // Output is 1, 3, then Done on separate lines. +``` + +**Use a lambda's final expression as its result.** Code after the lambda call +still runs. An explicit `return` inside the lambda is a compiler error. + + + +```toit +main: + classify := :: |value| + value < 0 ? "negative" : "non-negative" + print (classify.call -1) // Prints negative. + print "Still in main" +``` + + + +## Conditions and loops + +`if`, `else if`, `else`, and `while` work as you would expect, with indentation +in place of braces. A conditional expression uses `condition ? yes : no`. +Python readers should note that Toit also has a C-style `for` loop. + + + +```toit +main: + for i := 0; i < 4; i++: + if i == 1: continue + print i // Prints 0, 2, and 3. + + remaining := 3 + while remaining > 0: + remaining -= 1 + if remaining == 1: break + print remaining // Prints 1. +``` + +`break` and `continue` apply to `for` and `while`. For a fixed number of +repetitions, prefer `3.repeat:`; for collections, prefer `values.do:`. These +are methods taking blocks, so skip an iteration with `continue.repeat` or +`continue.do`. To exit a search early, use a block's non-local `return`, as +in `first-positive` above. See [control flow](/language/loops). + +## Collections and binary data + +Lists, maps, and sets are built in. Their elements can have different types; +`List` itself does not declare a type parameter for its elements. Use typed +block parameters or checks where element types matter. + +| Value | Literal | Common operations | +| --- | --- | --- | +| List | `[1, 2, 3]`; empty `[]` | `values[0]`, `values.add 4`, `values.size` | +| Map | `{"name": "Ada"}`; empty `{:}` | `labels["name"]`, `labels["name"] = "Grace"` | +| Set | `{1, 2, 3}`; empty `{}` | `seen.add 4`, `seen.contains 4` | +| Byte array | `#[0, 127, 255]`; empty `#[]` | `bytes[0]`, `bytes.size` | + +Python readers should note the empty map/set distinction. JavaScript readers +should use maps for dynamic keys and classes for structured objects. C/C++ +readers should use byte arrays and encoding APIs for binary data, rather than +assuming an object has a C struct's memory layout. + +Use slices such as `values[1..3]` for a range with an inclusive start and an +exclusive end. A map lookup using brackets throws if the key is missing; +`labels.get "name"` returns `null` when absent. Use `--if-absent` when you need +a different fallback. + +### Transform and combine values with blocks + +`map` transforms each element; `filter` selects elements; `reduce` combines +elements into one value. Their blocks supply results through their final +expressions. `do` is for performing an action on each element. + + + +```toit +main: + readings := [21, 26, 25] + warm := readings.filter: it >= 25 + labels := readings.map: "$it C" + total := readings.reduce --initial=0: |sum reading| sum + reading + print warm // Prints [26, 25]. + print (labels.join ", ") // Prints 21 C, 26 C, 25 C. + print (total.to-float / readings.size) // Prints 24.0. +``` + +The initial value makes this reduction valid for an empty list, but computing +an average still requires an explicit policy for empty input. `reduce` without +an initial value requires a nonempty collection. Other useful operations are +`any` and `every`, which test predicates without constructing a filtered list. + + + + + +```toit +import io + +main: + counts := {"warm": 2} + print (counts.get "cold") // Prints null. + print (counts.get "cold" --if-absent=: 0) // Prints 0; no entry added. + counts.update "cold" --init=0: it + 1 + print counts["cold"] // Prints 1. + + names ::= ["Ada"] + names.add "Grace" // A final binding still refers to a mutable list. + print names.size // Prints 2. + + bytes := ByteArray 4 --initial=0 + io.LITTLE-ENDIAN.put-uint32 bytes 0 0x1234 + print (io.LITTLE-ENDIAN.uint32 bytes 0) // Prints 4660. +``` + +`--if-absent` computes a fallback without inserting it. The `--init` form of +`update` creates a missing entry before updating it. Encoding methods specify +width and byte order explicitly. See [collections](/language/listsetmap). + + + +## Classes define the shape of objects + +A class declares fields and methods. Construct an instance by calling its class, +without `new`. In a constructor, `.value_` stores the corresponding argument +in the field. Elsewhere, methods can refer to fields directly; `this` names +the receiver explicitly when needed. + + + +```toit +class Counter: + value_/int := ? + + constructor .value_=0: + + value -> int: return value_ + + increment -> none: + value_ += 1 + +main: + counter := Counter 4 + counter.increment + print counter.value // Prints 5. +``` + +`:= ?` declares a mutable field that the constructor must initialize. +A typed field without `:=`, such as `name/string`, is final and must also be +initialized by the constructor. A trailing underscore marks a private member. +Use private fields when callers should go through methods instead of updating +the representation directly. + +`counter.value` invokes a zero-argument getter. Public fields have accessors +with the same call syntax, so you can replace a field with a computed getter +without changing readers. A setter is defined as `value= new-value:` and called +with `counter.value = new-value`. Kotlin readers will recognize the property-like +call syntax; Java readers do not need a separate `getValue()` convention. + +### Inheritance, interfaces, and constructors + +A class can extend one superclass and implement multiple interfaces. Interfaces +must be implemented explicitly: a class does not acquire an interface type +just because it has matching methods. This differs from TypeScript's structural +typing and Python's usual duck typing, and will be familiar from Java/Kotlin. + + + +```toit +interface Described: + description -> string + +class Device implements Described: + name_/string + + constructor .name_: + + description -> string: return name_ + +class Sensor extends Device: + constructor name/string: + super name + + description -> string: + return "Sensor: $(super)" + +main: + device/Described := Sensor "Kitchen" + print device.description // Prints Sensor: Kitchen. +``` + +In a constructor, `super` calls the superclass constructor. In an overriding +method, it calls the superclass implementation of that same method; do not +translate a JavaScript `super.description()` into `super.description`. + +Use `abstract class` when you want shared implementation with abstract methods +that subclasses must provide. Use `static` for methods and fields on the class +rather than its instances. Named constructors, such as `Counter.from-reading`, +give creation paths meaningful names. A factory constructor returns an existing +object or an object of another implementation rather than initializing a new +instance of its own class. + + + + + +```toit +class Counter: + value_/int := 0 + + constructor: + + constructor.from-reading reading/int: + value_ = reading + + value -> int: return value_ + + value= new-value/int: + if new-value < 0: throw "Negative count" + value_ = new-value + +main: + counter := Counter.from-reading 4 + counter.value = 5 + print counter.value // Prints 5. +``` + +The setter validates an assignment without changing its syntax at the call +site. See [classes and interfaces](/language/objects-constructors-inheritance-interfaces) +for static members, factories, and constructor initialization rules. + + + +## Errors and resources need explicit handling + +`throw` raises an exception. Exceptions are values; strings are common, and +objects are useful when a caller needs to distinguish kinds of failure. +`catch:` runs a block and returns the thrown value, or `null` on success. +The result is the exception, not the block's final expression. + + + +```toit +main: + value := 0 + error := catch: + value = int.parse "invalid" + if error: + print "Expected an integer" + else: + print "Read $value" +``` + +This prints `Expected an integer`. Throw a truthy value when using `if error` +to detect failure. `try:` with `finally:` performs cleanup, including when +execution leaves through an exception or a return. There is no combined +`try`/`catch` syntax; put a `try`/`finally` inside a `catch` when both are needed. + +Garbage collection reclaims unreachable memory. Unlike C++ destructors, it +does not provide deterministic resource cleanup. Put a resource's close +operation in `finally`, or use a library's scoped helper when one is available. + + + + + +```toit +main: + error := catch: + try: + print "Using resource" + throw "Read failed" + finally: + print "Close resource here" + if error: + print "Handled: $error" +``` + +The cleanup line runs before the handler prints the error. In real code, +replace the cleanup message with the resource's close operation. See +[exception handling](/language/exceptions). + + + +## Files are libraries + +Each `.toit` file is a library. Imports go at the top of the file. `import math` +imports an SDK library and makes its members available through the `math` +prefix. The core library, including `print`, `List`, and `int`, is available +automatically. + + + +```toit +import math + +main: + print (math.sqrt 9.0) // Prints 3.0. +``` + +`import .helpers` imports a sibling `helpers.toit`. Local imports make names +available without a prefix by default, whereas SDK and package imports use a +prefix. Use `as` to choose one explicitly, or `show` to select names. Unlike +C/C++, declarations do not need separate header files. + + + +Save these files in the same directory. Run `toit run main.toit`. + +**helpers.toit** + + + + +```toit +label name/string -> string: + return "Sensor: $name" +``` + +**main.toit** + + + + +```toit +import .helpers as helpers + +main: + print (helpers.label "Kitchen") // Prints Sensor: Kitchen. +``` + + + +Libraries distributed separately from the SDK are packages. Dependencies are +declared in `package.yaml`, and `package.lock` records resolved versions. +Imports are resolved before execution. Follow the +[package quick start](/language/package/pkgguide) to add a dependency, and +[imports](/language/imports) for namespace and path rules. + +## Waiting suspends a task + + + +```toit +main: + task:: + sleep --ms=10 + print "Background task finished" + print "Main task continues" +``` + +Toit tasks have independent stacks and share the program's objects. Waiting +operations such as `sleep` suspend the current task so another can run. Ordinary +functions can wait without an `async` declaration or an `await` at each caller. +This lets device and network code use ordinary loops and calls. JavaScript +and Python programmers can think of the sequential style of async/await, +with the difference that waiting works through ordinary functions and callers. +Kotlin readers do not need a `suspend` marker on each waiting function either. + +Scheduling within a program is cooperative. A CPU loop that never yields starves +other tasks. Conversely, a call that waits can allow another task to change shared +state; inspect library behavior when reasoning about an update spanning calls. +Tasks are not parallel threads and are distinct from independently running +containers. See [tasks and synchronization](/language/tasks). + +## Continue with a project + +You now have the language tools to read and write a Toit program: calls, +values, blocks, collections, objects, errors, imports, and tasks. Use the +[language reference](/language/reference) for rules and APIs as you need them. +To work with hardware, follow [Run on your device](/getstarted/device) and +then a [hardware or networking tutorial](/tutorials). diff --git a/docs/language/typeconversion.mdx b/docs/language/typeconversion.mdx index 7f9d13cf..aee1aa94 100644 --- a/docs/language/typeconversion.mdx +++ b/docs/language/typeconversion.mdx @@ -1,5 +1,9 @@ # Common conversions +Part of the [language reference](/language/reference), for readers who can already +follow a small Toit program. New to Toit? Start with the +[beginner tutorial](/language/beginner) or [Toit for programmers](/language/toitversus). + The following examples show how to convert between strings, integers, floating point numbers, characters (integer code points), and fixed point numbers in Toit. diff --git a/docs/menu.yaml b/docs/menu.yaml index cac614bd..fec5ae62 100644 --- a/docs/menu.yaml +++ b/docs/menu.yaml @@ -48,38 +48,55 @@ items: path: /language icon: language children: - - name: Common conversions - path: /language/typeconversion - - name: Definitions - path: /language/definitions - - name: Language comparison + - name: Toit for programmers path: /language/toitversus - - name: Style guide - path: /language/style - - name: Syntax fundamentals - path: /language/syntax - - name: Imports - path: /language/imports - - name: Classes - path: /language/objects-constructors-inheritance-interfaces - - name: Strings - path: /language/strings - - name: Blocks and lambdas - path: /language/blocks-and-lambdas - - name: Lists, byte arrays, sets and maps - path: /language/listsetmap - - name: Control flow - path: /language/loops - - name: Booleans - path: /language/booleans - - name: Mathematics - path: /language/math - - name: Bitwise operations - path: /language/bitmask - - name: Tasks - path: /language/tasks - - name: Exception handling - path: /language/exceptions + children: + - name: For JavaScripters + path: /language/from-javascript + - name: For Python programmers + path: /language/from-python + - name: For C and C++ programmers + path: /language/from-cpp + - name: Beginner lessons + path: /language/beginner + children: + - name: Lists and functions + path: /language/beginner/lists-and-functions + - name: Objects + path: /language/beginner/objects + - name: Language reference + path: /language/reference + children: + - name: Common conversions + path: /language/typeconversion + - name: Definitions + path: /language/definitions + - name: Style guide + path: /language/style + - name: Syntax fundamentals + path: /language/syntax + - name: Imports + path: /language/imports + - name: Classes + path: /language/objects-constructors-inheritance-interfaces + - name: Strings + path: /language/strings + - name: Blocks and lambdas + path: /language/blocks-and-lambdas + - name: Lists, byte arrays, sets and maps + path: /language/listsetmap + - name: Control flow + path: /language/loops + - name: Booleans + path: /language/booleans + - name: Mathematics + path: /language/math + - name: Bitwise operations + path: /language/bitmask + - name: Tasks + path: /language/tasks + - name: Exception handling + path: /language/exceptions - name: SDK path: /language/sdk children: