From 5260d6c8b3b32649a4fb197f68a6d9300efe89f7 Mon Sep 17 00:00:00 2001 From: Florian Loitsch Date: Sat, 12 Sep 2026 16:46:44 +0200 Subject: [PATCH 1/3] Restructure language introduction around reader experience --- docs/language/beginner.mdx | 344 +++++++ docs/language/bitmask.mdx | 4 + docs/language/blocks-and-lambdas.mdx | 4 + docs/language/booleans.mdx | 4 + docs/language/definitions.mdx | 4 + docs/language/exceptions.mdx | 4 + docs/language/from-cpp.mdx | 130 +++ docs/language/from-javascript.mdx | 251 +++++ docs/language/from-python.mdx | 126 +++ docs/language/imports.mdx | 4 + docs/language/index.mdx | 910 +----------------- docs/language/listsetmap.mdx | 4 + docs/language/loops.mdx | 4 + docs/language/math.mdx | 4 + ...ts-constructors-inheritance-interfaces.mdx | 4 + docs/language/reference.mdx | 16 + docs/language/strings.mdx | 4 + docs/language/style.mdx | 4 + docs/language/syntax.mdx | 4 + docs/language/tasks.mdx | 27 +- docs/language/toitversus.mdx | 347 ++++--- docs/language/typeconversion.mdx | 4 + docs/menu.yaml | 74 +- 23 files changed, 1188 insertions(+), 1093 deletions(-) create mode 100644 docs/language/beginner.mdx create mode 100644 docs/language/from-cpp.mdx create mode 100644 docs/language/from-javascript.mdx create mode 100644 docs/language/from-python.mdx create mode 100644 docs/language/reference.mdx diff --git a/docs/language/beginner.mdx b/docs/language/beginner.mdx new file mode 100644 index 00000000..b00697aa --- /dev/null +++ b/docs/language/beginner.mdx @@ -0,0 +1,344 @@ +# Beginner tutorial + +This tutorial assumes no Toit knowledge and explains the programming concepts +it uses. You will build a program that reports temperatures, first with a few +statements, then with a function and a class. You only need a computer; the +readings are sample data, so no sensor or ESP32 is required. + +Already comfortable programming? Start with [Toit for programmers](/language/toitversus). + +Examples are collapsed so you can read the explanation first. Expand each one +when you are ready to run it. Unless stated otherwise, each example is a complete +program: replace the contents of your file rather than appending it. + +## 1. Run your first program + +Install the [Toit SDK](https://github.com/toitlang/toit#readme) and make sure +`toit --help` works in your terminal. If you already use Jaguar, its bundled SDK +is available as `jag toit`; substitute `jag toit run` for `toit run` below. + +Create a plain-text file named `report.toit`. The program starts at `main:`. +Statements indented by two spaces underneath it form the body of `main`. +`print` displays a value in the terminal, and text between double quotes is a +*string*. + + + +```toit +main: + print "Temperature report" +``` + +In the terminal, change to the directory containing `report.toit`, then run: + +```sh +toit run report.toit +``` + +Expected output: + +```text +Temperature report +``` + + + +Change the message and run the command again. If the terminal cannot find `toit`, +check the SDK installation and your `PATH`. If it cannot find the file, check +your current directory. If the compiler reports an error, start at the first +reported file and line, and check the spelling and indentation. + +## 2. Name values and change them + +A *variable* gives a value a name. Write `temperature := 21` to declare one, +then `temperature = 24` to change it. The distinction catches accidental +redeclarations and assignments to names that do not exist. + +`$temperature` inside a string inserts the variable's value. Use `$(...)` for +a longer expression. Names can contain hyphens: `reading-count` is one name. +Use spaces around subtraction, as in `temperature - 2`. + + + + + +```toit +main: + temperature := 21 + print "First reading: $temperature C" + temperature = temperature + 3 + print "Next reading: $temperature C" +``` + +Expected output: + +```text +First reading: 21 C +Next reading: 24 C +``` + + + +Try changing the starting temperature. Predict both output lines before running. + +## 3. Make a decision + +An `if` statement runs its indented body only when its condition is true. +An `else` body runs otherwise. Comparisons such as `>=` (greater than or equal +to) and `==` (equal to) produce boolean values: `true` or `false`. +Use `==` to compare, and `=` to assign. + + + + + +```toit +main: + temperature := 24 + if temperature >= 25: + print "Warm" + else: + print "Comfortable" +``` + +This prints `Comfortable`. Change the value to `25` and it prints `Warm`. + + + +## 4. Process a list + +A *list* stores several values in order: `[21, 26, 24]`. `readings.size` gives +its length, and `readings[0]` gives the first element; indexes start at zero. + +`readings.do:` runs the indented code once per element. That code is a *block*. +`|temperature|` names the element for each run. The block can read and change +variables declared in its enclosing function. + + + + + +```toit +main: + readings := [21, 26, 24] + warm-count := 0 + readings.do: |temperature| + if temperature >= 25: + warm-count = warm-count + 1 + print "$warm-count of $(readings.size) readings are warm" +``` + +This prints `1 of 3 readings are warm`. + + + +Add `28` to the list and check that the output becomes +`2 of 4 readings are warm`. Try the empty list, `[]`, too. + +To repeat a known number of times, use `3.repeat:`. To repeat as long as a +condition holds, use `while condition:`. See [control flow](/language/loops) +when you need these alternatives. + +## 5. Put reusable work in a function + +A *function* names an operation. Parameters are names for the values it receives; +`return` gives a result back to the caller. A function definition sits at the +same indentation as `main`, not inside it. + +Define `count-warm readings --limit=25:` to accept readings and an optional named +parameter. Call it with `count-warm readings --limit=24`. Spaces separate call +arguments; Toit does not put commas or parentheses around the argument list. +Parentheses group a nested call, as in `print (count-warm readings)`. + + + + + +```toit +count-warm readings --limit=25: + count := 0 + readings.do: |temperature| + if temperature >= limit: + count = count + 1 + return count + +main: + readings := [21, 26, 24] + print (count-warm readings) + print (count-warm readings --limit=24) +``` + +Expected output: + +```text +1 +2 +``` + + + +A `return` inside a `do` block would leave the whole function immediately. +Here it belongs after the loop, so we count every reading before returning. + +## 6. Handle an empty input + +Our report should include an average. Adding the readings gives a total; +dividing by the count gives the average. Toit's integer division discards the +fraction, so convert the total with `to-float` before dividing. + +There is no average of an empty list. We will represent that absence with +`null` and test for it before printing. Only `null` and `false` are falsy in +Toit: a valid average of zero still passes `if average`. + + + + + +```toit +average-of readings -> float?: + if readings.size == 0: + return null + total := 0 + readings.do: |temperature| + total = total + temperature + return total.to-float / readings.size + +main: + average := average-of [21, 26, 25] + if average: + print "Average: $average C" + else: + print "No readings" +``` + +This prints `Average: 24.0 C`. Replace the list with `[]` to get `No readings`. +The `-> float?` annotation says that the result is a floating-point number or +`null`. Toit checks type annotations at runtime; they also help the editor +explain your code. + + + +## 7. Finish the report with a class + +A *class* groups data with operations on that data. Each *object* made from a +class has its own fields. A *method* is a function attached to an object. + +Our `TemperatureReport` will hold a list of readings and a warm threshold. Its +`print-summary` method will use the two functions we already wrote. This is +useful when we want several reports with different data and thresholds. + +Fields are declared in the class. In `constructor .readings_ --limit=25:`, +`.readings_` automatically stores the first argument in that field. The named +parameter is assigned explicitly to `limit_`. The `_` suffix marks these fields +private. `:= ?` requires the constructor to initialize the mutable field. + + + + + +```toit +count-warm readings/List --limit/int=25 -> int: + count := 0 + readings.do: |temperature/int| + if temperature >= limit: + count = count + 1 + return count + +average-of readings/List -> float?: + if readings.size == 0: + return null + total := 0 + readings.do: |temperature/int| + total = total + temperature + return total.to-float / readings.size + +class TemperatureReport: + readings_/List + limit_/int := ? + + constructor .readings_ --limit/int=25: + limit_ = limit + + print-summary -> none: + average := average-of readings_ + if not average: + print "No readings" + return + warm-count := count-warm readings_ --limit=limit_ + print "Readings: $(readings_.size)" + print "Average: $average C" + print "Warm readings: $warm-count" + +main: + report := TemperatureReport [21, 26, 25] + report.print-summary + empty-report := TemperatureReport [] + empty-report.print-summary +``` + +Run it with `toit run report.toit`. Expected output: + +```text +Readings: 3 +Average: 24.0 C +Warm readings: 2 +No readings +``` + + + +`TemperatureReport [21, 26, 25]` constructs an object. +`report.print-summary` calls its method without parentheses. `/List` and `/int` +are type annotations; `-> none` says the method does not return a value. +The `readings_` field cannot be reassigned, but its list can still be changed. + +## 8. Check your understanding + +Try these changes to the complete program: + +1. Set the report's limit to `26`. The warm count should become `1`. +2. Use `[0, 0]`. The average should be `0.0`, not `No readings`. +3. Add a function that finds the highest reading, returning `null` for an empty list. + + + + + +```toit +highest-of readings/List -> int?: + if readings.size == 0: + return null + highest := readings[0] + readings.do: |temperature/int| + if temperature > highest: + highest = temperature + return highest + +main: + print (highest-of [21, 26, 25]) + print (highest-of []) +``` + +Expected output: + +```text +26 +null +``` + + + +## Continue from here + +You can now run a program, name and update values, make decisions, process a +list, write a function, handle missing data, and build an object. + +To grow the program, move the helper functions to another file using +[imports](/language/imports). Learn [maps and other collections](/language/listsetmap) +for data with keys, [exceptions](/language/exceptions) for failed operations, and +[tasks](/language/tasks) for concurrent activities. These are topic guides you +can consult when needed. + +For a device project, follow [Run on your device](/getstarted/device), then the +[first LED tutorial](/tutorials/hardware/led). To revisit an unfamiliar construct, +use the [language reference](/language/reference). 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..3fea3c6b --- /dev/null +++ b/docs/language/from-cpp.mdx @@ -0,0 +1,130 @@ +# Toit for C and C++ programmers + +For readers familiar with native code, memory management, and threads. Toit +runs in a virtual machine with garbage-collected objects. The main adjustment +is how you reason about memory, resources, and concurrency. +See [Toit for programmers](/language/toitversus) for calls, types, and collections. + +## 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. + +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 `::` lambda has its own return scope. + + + + + +```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. + + + +## 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. diff --git a/docs/language/from-javascript.mdx b/docs/language/from-javascript.mdx new file mode 100644 index 00000000..b6f5ff87 --- /dev/null +++ b/docs/language/from-javascript.mdx @@ -0,0 +1,251 @@ +# Toit for JavaScripters + +For JavaScript and TypeScript programmers who already know functions, objects, +and asynchronous code. Read this to identify assumptions you need to change; +use [Toit for programmers](/language/toitversus) for the shared language overview. +Expand the examples when you want to try a rule. 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). + +## 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; `return` +in a lambda exits that lambda. 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..be530bcb --- /dev/null +++ b/docs/language/from-python.mdx @@ -0,0 +1,126 @@ +# 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. +Read [Toit for programmers](/language/toitversus) for the broader overview. + +## 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). 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..0f53c4ed 100644 --- a/docs/language/index.mdx +++ b/docs/language/index.mdx @@ -1,896 +1,28 @@ -# 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 | +| --- | --- | --- | +| New to programming, or want a guided first program | [Beginner tutorial](/language/beginner) | Build and run a temperature report, from variables to functions and a small class. | +| Already comfortable programming | [Toit for programmers](/language/toitversus) | How calls, types, blocks, objects, and tasks work, and what those choices imply. | +| Coming from JavaScript or TypeScript | [Toit for JavaScripters](/language/from-javascript) | Replace assumptions about callbacks, promises, objects, truthiness, and numbers. | +| Coming from Python | [Toit for Python programmers](/language/from-python) | Understand the differences behind the familiar indentation. | +| Coming from C or C++ | [Toit for C and C++ programmers](/language/from-cpp) | Move from native memory and threads to managed objects and cooperative tasks. | -Now, let's get started with some programming! +## Look up a language feature -## 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. -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`: +## Use Toit in a project -``` -main: - print "Hello World!" -``` - -you can run it from the command line like this: - - - - -```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..b35abbd2 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. 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..8071bf94 100644 --- a/docs/language/toitversus.mdx +++ b/docs/language/toitversus.mdx @@ -1,159 +1,188 @@ -# 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 guide assumes you know variables, functions, classes, and collections in +another language. It focuses on the choices that change how you write programs. +For a step-by-step first program, use the [beginner tutorial](/language/beginner). + +For comparisons with your own language, see [JavaScript and TypeScript](/language/from-javascript), +[Python](/language/from-python), or [C and C++](/language/from-cpp). + +## 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 + + + +```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. + +## Types are optional, but checked + +The `/int` parameters and `-> int` return annotation above 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 interfaces and fields to document and enforce expectations. +See [types](/language/definitions#type). + +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. + +## Blocks are part of control flow + + + + + +```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. + +A lambda starts with `::`, can outlive the call that creates it, and has its own +return scope: + + + + + +```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`. + +## Classes define the shape of objects + + + + + +```toit +class Counter: + value/int := 0 + + increment -> none: + value = value + 1 + +main: + counter := Counter + counter.increment + print counter.value +``` + + + +This prints `1`. Calling the class constructs an instance, without `new`. +Fields are declared in the class; methods can refer to them without a `this.` +prefix. A trailing underscore marks a private member. For data with keys chosen +at runtime, use a map such as `{"temperature": 23}` and access it with brackets. +See [classes and interfaces](/language/objects-constructors-inheritance-interfaces). + +## 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. + +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). + +## Errors and resources need explicit handling + +`throw` raises an exception. `catch:` runs a block and returns the thrown value, +or `null` on success. `try:` with `finally:` performs cleanup, including when +execution leaves through an exception. There is no combined `try`/`catch` syntax. + +Garbage collection reclaims unreachable memory, but does not replace explicit +resource cleanup. Pair resource acquisition with the API's close operation in +`finally`. See [exception handling](/language/exceptions). + +## Files are libraries + +Each `.toit` file is a library. `import .helpers` imports a sibling file; +`import math` imports an SDK library. The core library is available automatically. +Imports and package dependencies are resolved before execution, so organizing a +program does not require runtime module loading. + +Continue with [imports](/language/imports), the [package quick start](/language/package/pkgguide), +or the [language reference](/language/reference). To run the examples locally, +follow the setup at the start of the [beginner tutorial](/language/beginner). 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..eb32a09c 100644 --- a/docs/menu.yaml +++ b/docs/menu.yaml @@ -48,38 +48,50 @@ items: path: /language icon: language children: - - name: Common conversions - path: /language/typeconversion - - name: Definitions - path: /language/definitions - - name: Language comparison + - name: Beginner tutorial + path: /language/beginner + - 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: 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: From 43910109050bc25f8cedab82e2430034e14aa6ec Mon Sep 17 00:00:00 2001 From: Florian Loitsch Date: Sat, 12 Sep 2026 17:02:20 +0200 Subject: [PATCH 2/3] Keep teaching examples visible and expand supplementary examples --- README.md | 9 ++ docs/language/beginner.mdx | 33 +---- docs/language/from-cpp.mdx | 19 +-- docs/language/from-javascript.mdx | 57 +++----- docs/language/from-python.mdx | 16 +- docs/language/strings.mdx | 14 +- docs/language/toitversus.mdx | 236 +++++++++++++++++++++++++++--- 7 files changed, 265 insertions(+), 119 deletions(-) diff --git a/README.md b/README.md index 8940df20..06ea42f1 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,15 @@ The documentation pages are written in `.mdx` which are [markdown](https://www.markdownguide.org/basic-syntax/) files that can also import react components. +## 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 index b00697aa..347a93b2 100644 --- a/docs/language/beginner.mdx +++ b/docs/language/beginner.mdx @@ -7,9 +7,8 @@ readings are sample data, so no sensor or ESP32 is required. Already comfortable programming? Start with [Toit for programmers](/language/toitversus). -Examples are collapsed so you can read the explanation first. Expand each one -when you are ready to run it. Unless stated otherwise, each example is a complete -program: replace the contents of your file rather than appending it. +Each example is a complete program: replace the contents of your file rather +than appending it. The exercise solution is collapsed so you can try it first. ## 1. Run your first program @@ -22,8 +21,6 @@ Statements indented by two spaces underneath it form the body of `main`. `print` displays a value in the terminal, and text between double quotes is a *string*. - - ```toit main: print "Temperature report" @@ -41,8 +38,6 @@ Expected output: Temperature report ``` - - Change the message and run the command again. If the terminal cannot find `toit`, check the SDK installation and your `PATH`. If it cannot find the file, check your current directory. If the compiler reports an error, start at the first @@ -58,8 +53,6 @@ redeclarations and assignments to names that do not exist. a longer expression. Names can contain hyphens: `reading-count` is one name. Use spaces around subtraction, as in `temperature - 2`. - - ```toit @@ -77,8 +70,6 @@ First reading: 21 C Next reading: 24 C ``` - - Try changing the starting temperature. Predict both output lines before running. ## 3. Make a decision @@ -88,8 +79,6 @@ An `else` body runs otherwise. Comparisons such as `>=` (greater than or equal to) and `==` (equal to) produce boolean values: `true` or `false`. Use `==` to compare, and `=` to assign. - - ```toit @@ -103,8 +92,6 @@ main: This prints `Comfortable`. Change the value to `25` and it prints `Warm`. - - ## 4. Process a list A *list* stores several values in order: `[21, 26, 24]`. `readings.size` gives @@ -114,8 +101,6 @@ its length, and `readings[0]` gives the first element; indexes start at zero. `|temperature|` names the element for each run. The block can read and change variables declared in its enclosing function. - - ```toit @@ -130,8 +115,6 @@ main: This prints `1 of 3 readings are warm`. - - Add `28` to the list and check that the output becomes `2 of 4 readings are warm`. Try the empty list, `[]`, too. @@ -150,8 +133,6 @@ parameter. Call it with `count-warm readings --limit=24`. Spaces separate call arguments; Toit does not put commas or parentheses around the argument list. Parentheses group a nested call, as in `print (count-warm readings)`. - - ```toit @@ -175,8 +156,6 @@ Expected output: 2 ``` - - A `return` inside a `do` block would leave the whole function immediately. Here it belongs after the loop, so we count every reading before returning. @@ -190,8 +169,6 @@ There is no average of an empty list. We will represent that absence with `null` and test for it before printing. Only `null` and `false` are falsy in Toit: a valid average of zero still passes `if average`. - - ```toit @@ -216,8 +193,6 @@ The `-> float?` annotation says that the result is a floating-point number or `null`. Toit checks type annotations at runtime; they also help the editor explain your code. - - ## 7. Finish the report with a class A *class* groups data with operations on that data. Each *object* made from a @@ -232,8 +207,6 @@ Fields are declared in the class. In `constructor .readings_ --limit=25:`, parameter is assigned explicitly to `limit_`. The `_` suffix marks these fields private. `:= ?` requires the constructor to initialize the mutable field. - - ```toit @@ -285,8 +258,6 @@ Warm readings: 2 No readings ``` - - `TemperatureReport [21, 26, 25]` constructs an object. `report.print-summary` calls its method without parentheses. `/List` and `/int` are type annotations; `-> none` says the method does not return a value. diff --git a/docs/language/from-cpp.mdx b/docs/language/from-cpp.mdx index 3fea3c6b..a3cd7085 100644 --- a/docs/language/from-cpp.mdx +++ b/docs/language/from-cpp.mdx @@ -16,8 +16,6 @@ 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: @@ -41,8 +39,6 @@ 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 @@ -65,9 +61,8 @@ invokes `read` rather than producing a method pointer. 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 `::` lambda has its own return scope. - - +enclosing function, unlike a C++ lambda. A Toit `::` lambda uses its final +expression as its result and cannot contain an explicit `return`. @@ -85,8 +80,6 @@ main: This prints `4`. The `do` method implements iteration while the block retains the enclosing function's return behavior. - - ## Tasks cooperate within a program Each task has a call stack, but tasks in a program share objects and take turns @@ -100,8 +93,6 @@ 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 @@ -118,8 +109,6 @@ main: 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 @@ -128,3 +117,7 @@ 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 index b6f5ff87..dafc7e30 100644 --- a/docs/language/from-javascript.mdx +++ b/docs/language/from-javascript.mdx @@ -3,8 +3,7 @@ For JavaScript and TypeScript programmers who already know functions, objects, and asynchronous code. Read this to identify assumptions you need to change; use [Toit for programmers](/language/toitversus) for the shared language overview. -Expand the examples when you want to try a rule. Each Toit example is a complete -program you can run using the [local setup](/language/beginner#1-run-your-first-program). +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 @@ -16,8 +15,6 @@ 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 "] @@ -31,12 +28,29 @@ main: 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 @@ -44,11 +58,10 @@ The `:` in `values.do: |value|` introduces a block. Like a callback passed to `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; `return` -in a lambda exits that lambda. This distinction lets library iteration behave -like a loop while supporting callbacks with a longer lifetime. - - +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. @@ -66,8 +79,6 @@ main: 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. @@ -82,8 +93,6 @@ 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 @@ -99,8 +108,6 @@ main: `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) @@ -119,8 +126,6 @@ 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 @@ -138,8 +143,6 @@ Expected output: fallback ``` - - ## Numbers distinguish integers from floats Toit has signed 64-bit `int` values and floating-point `float` values. @@ -148,8 +151,6 @@ 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 @@ -169,8 +170,6 @@ Expected output: 42 ``` - - See [numbers](/language/math) and [conversions](/language/typeconversion). ## Choose a class or a map @@ -184,8 +183,6 @@ 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 @@ -207,8 +204,6 @@ main: 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 @@ -220,8 +215,6 @@ is `string?`. See [types](/language/definitions#type) and thrown. Use `try:` with `finally:` for cleanup. This is different syntax from JavaScript's `try`/`catch`/`finally` statement. - - ```toit @@ -234,8 +227,6 @@ main: 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. diff --git a/docs/language/from-python.mdx b/docs/language/from-python.mdx index be530bcb..1b689b4a 100644 --- a/docs/language/from-python.mdx +++ b/docs/language/from-python.mdx @@ -14,8 +14,6 @@ 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" @@ -27,8 +25,6 @@ main: This prints `Hello Ada` and `Welcome Grace`. - - ## Test emptiness explicitly Only `false` and `null` are falsy in Toit. Zero, empty strings, and empty @@ -40,8 +36,6 @@ 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 @@ -66,8 +60,6 @@ The list is empty 2.5 ``` - - ## Iteration blocks can return from their function Use `items.do: |item|` to iterate, and `items.map: |item|` to transform a list. @@ -75,8 +67,6 @@ 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 @@ -93,8 +83,6 @@ main: 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. @@ -124,3 +112,7 @@ 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/strings.mdx b/docs/language/strings.mdx index b35abbd2..e43b8a4a 100644 --- a/docs/language/strings.mdx +++ b/docs/language/strings.mdx @@ -357,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: @@ -371,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/toitversus.mdx b/docs/language/toitversus.mdx index 8071bf94..be5dca36 100644 --- a/docs/language/toitversus.mdx +++ b/docs/language/toitversus.mdx @@ -20,8 +20,6 @@ For comparisons with your own language, see [JavaScript and TypeScript](/languag ## Read calls before translating syntax - - ```toit add a/int b/int -> int: return a + b @@ -32,8 +30,6 @@ main: 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: @@ -49,9 +45,141 @@ 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. + + + +## 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)"`. + + + +**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 above specify runtime +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. @@ -65,11 +193,47 @@ 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. -## Blocks are part of control flow + + +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 + + ```toit first-positive values: @@ -82,21 +246,17 @@ 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. -A lambda starts with `::`, can outlive the call that creates it, and has its own -return scope: +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 @@ -106,17 +266,53 @@ main: 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`. -## Classes define the shape of objects + + +**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" +``` + + + +## Classes define the shape of objects + + ```toit class Counter: @@ -131,8 +327,6 @@ main: print counter.value ``` - - This prints `1`. Calling the class constructs an instance, without `new`. Fields are declared in the class; methods can refer to them without a `this.` prefix. A trailing underscore marks a private member. For data with keys chosen @@ -143,8 +337,6 @@ See [classes and interfaces](/language/objects-constructors-inheritance-interfac - - ```toit main: task:: @@ -153,8 +345,6 @@ main: 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. From 5ff617de165f42263394b7b215c67c2b68b88ca4 Mon Sep 17 00:00:00 2001 From: Florian Loitsch Date: Sat, 12 Sep 2026 18:15:33 +0200 Subject: [PATCH 3/3] Expand shared programmer tour and pace beginner lessons --- README.md | 13 + docs/language/beginner.mdx | 336 ++++-------- .../language/beginner/lists-and-functions.mdx | 215 ++++++++ docs/language/beginner/objects.mdx | 194 +++++++ docs/language/from-cpp.mdx | 27 +- docs/language/from-javascript.mdx | 10 +- docs/language/from-python.mdx | 4 +- docs/language/index.mdx | 16 +- docs/language/toitversus.mdx | 494 ++++++++++++++++-- docs/menu.yaml | 9 +- 10 files changed, 1043 insertions(+), 275 deletions(-) create mode 100644 docs/language/beginner/lists-and-functions.mdx create mode 100644 docs/language/beginner/objects.mdx diff --git a/README.md b/README.md index 06ea42f1..058c26a6 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,19 @@ 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 diff --git a/docs/language/beginner.mdx b/docs/language/beginner.mdx index 347a93b2..d74c6d41 100644 --- a/docs/language/beginner.mdx +++ b/docs/language/beginner.mdx @@ -1,315 +1,205 @@ # Beginner tutorial -This tutorial assumes no Toit knowledge and explains the programming concepts -it uses. You will build a program that reports temperatures, first with a few -statements, then with a function and a class. You only need a computer; the -readings are sample data, so no sensor or ESP32 is required. +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). -Already comfortable programming? 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. -Each example is a complete program: replace the contents of your file rather -than appending it. The exercise solution is collapsed so you can try it first. +| 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) and make sure -`toit --help` works in your terminal. If you already use Jaguar, its bundled SDK -is available as `jag toit`; substitute `jag toit run` for `toit run` below. +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. -Create a plain-text file named `report.toit`. The program starts at `main:`. -Statements indented by two spaces underneath it form the body of `main`. -`print` displays a value in the terminal, and text between double quotes is a -*string*. +In a text editor, create a file named `report.toit` containing these two lines: ```toit main: print "Temperature report" ``` -In the terminal, change to the directory containing `report.toit`, then run: +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 ``` -Expected output: +You should see: ```text Temperature report ``` -Change the message and run the command again. If the terminal cannot find `toit`, -check the SDK installation and your `PATH`. If it cannot find the file, check -your current directory. If the compiler reports an error, start at the first -reported file and line, and check the spelling and indentation. - -## 2. Name values and change them +`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. -A *variable* gives a value a name. Write `temperature := 21` to declare one, -then `temperature = 24` to change it. The distinction catches accidental -redeclarations and assignments to names that do not exist. +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. -`$temperature` inside a string inserts the variable's value. Use `$(...)` for -a longer expression. Names can contain hyphens: `reading-count` is one name. -Use spaces around subtraction, as in `temperature - 2`. + - +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. -```toit -main: - temperature := 21 - print "First reading: $temperature C" - temperature = temperature + 3 - print "Next reading: $temperature C" -``` - -Expected output: - -```text -First reading: 21 C -Next reading: 24 C -``` + -Try changing the starting temperature. Predict both output lines before running. +## 2. Give a value a name -## 3. Make a decision +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. -An `if` statement runs its indented body only when its condition is true. -An `else` body runs otherwise. Comparisons such as `>=` (greater than or equal -to) and `==` (equal to) produce boolean values: `true` or `false`. -Use `==` to compare, and `=` to assign. +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 := 24 - if temperature >= 25: - print "Warm" - else: - print "Comfortable" + temperature := 21 + print temperature ``` -This prints `Comfortable`. Change the value to `25` and it prints `Warm`. +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. -## 4. Process a list +Change `21` to `24`, save, and run again. Then try `print "temperature"` and +compare the output. Put `print temperature` back before continuing. -A *list* stores several values in order: `[21, 26, 24]`. `readings.size` gives -its length, and `readings[0]` gives the first element; indexes start at zero. +## 3. Change the value -`readings.do:` runs the indented code once per element. That code is a *block*. -`|temperature|` names the element for each run. The block can read and change -variables declared in its enclosing function. +Once a variable exists, use `=` to replace its value. Use `:=` only when +introducing the name. ```toit main: - readings := [21, 26, 24] - warm-count := 0 - readings.do: |temperature| - if temperature >= 25: - warm-count = warm-count + 1 - print "$warm-count of $(readings.size) readings are warm" + temperature := 21 + print temperature + temperature = 24 + print temperature ``` -This prints `1 of 3 readings are warm`. +The output is: -Add `28` to the list and check that the output becomes -`2 of 4 readings are warm`. Try the empty list, `[]`, too. +```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. -To repeat a known number of times, use `3.repeat:`. To repeat as long as a -condition holds, use `while condition:`. See [control flow](/language/loops) -when you need these alternatives. +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. -## 5. Put reusable work in a function +Try adding five instead. Predict the second line before running the program. -A *function* names an operation. Parameters are names for the values it receives; -`return` gives a result back to the caller. A function definition sits at the -same indentation as `main`, not inside it. +## 4. Put the value in a message -Define `count-warm readings --limit=25:` to accept readings and an optional named -parameter. Call it with `count-warm readings --limit=24`. Spaces separate call -arguments; Toit does not put commas or parentheses around the argument list. -Parentheses group a nested call, as in `print (count-warm readings)`. +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 -count-warm readings --limit=25: - count := 0 - readings.do: |temperature| - if temperature >= limit: - count = count + 1 - return count - main: - readings := [21, 26, 24] - print (count-warm readings) - print (count-warm readings --limit=24) + temperature := 21 + print "Temperature: $temperature C" ``` -Expected output: +This prints: ```text -1 -2 +Temperature: 21 C ``` -A `return` inside a `do` block would leave the whole function immediately. -Here it belongs after the loop, so we count every reading before returning. - -## 6. Handle an empty input +Change the value to `24`. You only need to change the line that sets the +temperature; the message uses that value automatically. -Our report should include an average. Adding the readings gives a total; -dividing by the count gives the average. Toit's integer division discards the -fraction, so convert the total with `to-float` before dividing. +## 5. Decide what to print -There is no average of an empty list. We will represent that absence with -`null` and test for it before printing. Only `null` and `false` are falsy in -Toit: a valid average of zero still passes `if average`. +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 -average-of readings -> float?: - if readings.size == 0: - return null - total := 0 - readings.do: |temperature| - total = total + temperature - return total.to-float / readings.size - main: - average := average-of [21, 26, 25] - if average: - print "Average: $average C" + temperature := 24 + print "Temperature: $temperature C" + if temperature >= 25: + print "Warm" else: - print "No readings" -``` - -This prints `Average: 24.0 C`. Replace the list with `[]` to get `No readings`. -The `-> float?` annotation says that the result is a floating-point number or -`null`. Toit checks type annotations at runtime; they also help the editor -explain your code. - -## 7. Finish the report with a class - -A *class* groups data with operations on that data. Each *object* made from a -class has its own fields. A *method* is a function attached to an object. - -Our `TemperatureReport` will hold a list of readings and a warm threshold. Its -`print-summary` method will use the two functions we already wrote. This is -useful when we want several reports with different data and thresholds. - -Fields are declared in the class. In `constructor .readings_ --limit=25:`, -`.readings_` automatically stores the first argument in that field. The named -parameter is assigned explicitly to `limit_`. The `_` suffix marks these fields -private. `:= ?` requires the constructor to initialize the mutable field. - - - -```toit -count-warm readings/List --limit/int=25 -> int: - count := 0 - readings.do: |temperature/int| - if temperature >= limit: - count = count + 1 - return count - -average-of readings/List -> float?: - if readings.size == 0: - return null - total := 0 - readings.do: |temperature/int| - total = total + temperature - return total.to-float / readings.size - -class TemperatureReport: - readings_/List - limit_/int := ? - - constructor .readings_ --limit/int=25: - limit_ = limit - - print-summary -> none: - average := average-of readings_ - if not average: - print "No readings" - return - warm-count := count-warm readings_ --limit=limit_ - print "Readings: $(readings_.size)" - print "Average: $average C" - print "Warm readings: $warm-count" - -main: - report := TemperatureReport [21, 26, 25] - report.print-summary - empty-report := TemperatureReport [] - empty-report.print-summary + print "Comfortable" ``` -Run it with `toit run report.toit`. Expected output: +This prints: ```text -Readings: 3 -Average: 24.0 C -Warm readings: 2 -No readings +Temperature: 24 C +Comfortable ``` -`TemperatureReport [21, 26, 25]` constructs an object. -`report.print-summary` calls its method without parentheses. `/List` and `/int` -are type annotations; `-> none` says the method does not return a value. -The `readings_` field cannot be reassigned, but its list can still be changed. +`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. -## 8. Check your understanding +The comparison is called a *condition*. Its result is `true` for yes or `false` +for no. Those two values are called *booleans*. -Try these changes to the complete program: +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. -1. Set the report's limit to `26`. The warm count should become `1`. -2. Use `[0, 0]`. The average should be `0.0`, not `No readings`. -3. Add a function that finds the highest reading, returning `null` for an empty list. +## 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 -highest-of readings/List -> int?: - if readings.size == 0: - return null - highest := readings[0] - readings.do: |temperature/int| - if temperature > highest: - highest = temperature - return highest - main: - print (highest-of [21, 26, 25]) - print (highest-of []) + temperature := -5 + if temperature < 0: + print "Freezing" + else: + print "Not freezing" ``` -Expected output: - -```text -26 -null -``` +This prints `Freezing`. With `0` or `5`, it prints `Not freezing`. -## Continue from here - -You can now run a program, name and update values, make decisions, process a -list, write a function, handle missing data, and build an object. - -To grow the program, move the helper functions to another file using -[imports](/language/imports). Learn [maps and other collections](/language/listsetmap) -for data with keys, [exceptions](/language/exceptions) for failed operations, and -[tasks](/language/tasks) for concurrent activities. These are topic guides you -can consult when needed. - -For a device project, follow [Run on your device](/getstarted/device), then the -[first LED tutorial](/tutorials/hardware/led). To revisit an unfamiliar construct, -use the [language reference](/language/reference). +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/from-cpp.mdx b/docs/language/from-cpp.mdx index a3cd7085..35893709 100644 --- a/docs/language/from-cpp.mdx +++ b/docs/language/from-cpp.mdx @@ -1,9 +1,10 @@ # Toit for C and C++ programmers -For readers familiar with native code, memory management, and threads. Toit -runs in a virtual machine with garbage-collected objects. The main adjustment -is how you reason about memory, resources, and concurrency. -See [Toit for programmers](/language/toitversus) for calls, types, and collections. +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 @@ -59,6 +60,20 @@ 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 @@ -78,7 +93,9 @@ main: ``` This prints `4`. The `do` method implements iteration while the block retains -the enclosing function's return behavior. +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 diff --git a/docs/language/from-javascript.mdx b/docs/language/from-javascript.mdx index dafc7e30..3f4b2a2f 100644 --- a/docs/language/from-javascript.mdx +++ b/docs/language/from-javascript.mdx @@ -1,9 +1,13 @@ # Toit for JavaScripters For JavaScript and TypeScript programmers who already know functions, objects, -and asynchronous code. Read this to identify assumptions you need to change; -use [Toit for programmers](/language/toitversus) for the shared language overview. -Each Toit example is a complete program you can run using the [local setup](/language/beginner#1-run-your-first-program). +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 diff --git a/docs/language/from-python.mdx b/docs/language/from-python.mdx index 1b689b4a..6b0d472c 100644 --- a/docs/language/from-python.mdx +++ b/docs/language/from-python.mdx @@ -2,7 +2,9 @@ For readers comfortable writing Python functions and classes. Toit's indentation will look familiar, but calls, control flow, and values have different rules. -Read [Toit for programmers](/language/toitversus) for the broader overview. +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 diff --git a/docs/language/index.mdx b/docs/language/index.mdx index 0f53c4ed..15c93645 100644 --- a/docs/language/index.mdx +++ b/docs/language/index.mdx @@ -8,11 +8,17 @@ language without connecting hardware. | Your experience | Start here | What you will learn | | --- | --- | --- | -| New to programming, or want a guided first program | [Beginner tutorial](/language/beginner) | Build and run a temperature report, from variables to functions and a small class. | -| Already comfortable programming | [Toit for programmers](/language/toitversus) | How calls, types, blocks, objects, and tasks work, and what those choices imply. | -| Coming from JavaScript or TypeScript | [Toit for JavaScripters](/language/from-javascript) | Replace assumptions about callbacks, promises, objects, truthiness, and numbers. | -| Coming from Python | [Toit for Python programmers](/language/from-python) | Understand the differences behind the familiar indentation. | -| Coming from C or C++ | [Toit for C and C++ programmers](/language/from-cpp) | Move from native memory and threads to managed objects and cooperative tasks. | +| 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. | + +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. + +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. ## Look up a language feature diff --git a/docs/language/toitversus.mdx b/docs/language/toitversus.mdx index be5dca36..5e54de2c 100644 --- a/docs/language/toitversus.mdx +++ b/docs/language/toitversus.mdx @@ -1,11 +1,22 @@ # Toit for programmers -This guide assumes you know variables, functions, classes, and collections in -another language. It focuses on the choices that change how you write programs. -For a step-by-step first program, use the [beginner tutorial](/language/beginner). - -For comparisons with your own language, see [JavaScript and TypeScript](/language/from-javascript), -[Python](/language/from-python), or [C and C++](/language/from-cpp). +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 @@ -20,6 +31,13 @@ For comparisons with your own language, see [JavaScript and TypeScript](/languag ## 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 @@ -86,6 +104,36 @@ 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: @@ -104,6 +152,17 @@ 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; @@ -185,14 +244,38 @@ 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 interfaces and fields to document and enforce expectations. +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 @@ -233,6 +316,14 @@ 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 @@ -252,6 +343,49 @@ A block's last expression supplies its result to its caller; it does not need 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`: @@ -310,28 +444,326 @@ 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 := 0 + value_/int := ? + + constructor .value_=0: + + value -> int: return value_ increment -> none: - value = value + 1 + value_ += 1 main: - counter := Counter + counter := Counter 4 counter.increment - print counter.value + 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. ``` -This prints `1`. Calling the class constructs an instance, without `new`. -Fields are declared in the class; methods can refer to them without a `this.` -prefix. A trailing underscore marks a private member. For data with keys chosen -at runtime, use a map such as `{"temperature": 23}` and access it with brackets. -See [classes and interfaces](/language/objects-constructors-inheritance-interfaces). +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 @@ -348,7 +780,10 @@ main: 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. +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 @@ -356,23 +791,10 @@ 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). -## Errors and resources need explicit handling - -`throw` raises an exception. `catch:` runs a block and returns the thrown value, -or `null` on success. `try:` with `finally:` performs cleanup, including when -execution leaves through an exception. There is no combined `try`/`catch` syntax. - -Garbage collection reclaims unreachable memory, but does not replace explicit -resource cleanup. Pair resource acquisition with the API's close operation in -`finally`. See [exception handling](/language/exceptions). - -## Files are libraries - -Each `.toit` file is a library. `import .helpers` imports a sibling file; -`import math` imports an SDK library. The core library is available automatically. -Imports and package dependencies are resolved before execution, so organizing a -program does not require runtime module loading. +## Continue with a project -Continue with [imports](/language/imports), the [package quick start](/language/package/pkgguide), -or the [language reference](/language/reference). To run the examples locally, -follow the setup at the start of the [beginner tutorial](/language/beginner). +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/menu.yaml b/docs/menu.yaml index eb32a09c..fec5ae62 100644 --- a/docs/menu.yaml +++ b/docs/menu.yaml @@ -48,8 +48,6 @@ items: path: /language icon: language children: - - name: Beginner tutorial - path: /language/beginner - name: Toit for programmers path: /language/toitversus children: @@ -59,6 +57,13 @@ items: 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: