# Basics

URL: /docs/tour/basics
Markdown: /docs/tour/basics.md

> Lexical rules, bindings, functions, control flow, operators, strings, lambdas, loops, and modules in Sakura Lang.

> **Pre-alpha**
>
> The source-built compiler runs the core language and much of M3 on JavaScript: contracts, typed effects and failures, std handlers, permissions, LSP and MCP. M3 is still in progress; no release or stable compatibility contract exists. See the [roadmap](/docs/roadmap) for implemented features and remaining gates.

Sakura Lang has a small surface: braces, `fn`, `let`/`var`, `match`, `if`/`else`. The keywords are:

```text
fn let var type error effect handler with try catch fail if else match for in while
return break continue import pub opaque as test property requires ensures where true false
```

`verify proof ghost control resume region spawn actor interface impl` are reserved for later and rejected today (`V0007`). A reserved word is still accepted directly after `.` or `fn`, so `Task.spawn(s, body)` is legal. Two names are special only in one place: `it` inside a `where` refinement and `result` inside an `ensures` clause. There is no `null`, no `class`, no `self`, no exceptions, no `async`/`await`, no macros, and no operator overloading.

## Names and casing [#names-and-casing]

Casing is part of the grammar. Values, functions, modules, fields, and effect-row variables are `snake_case`. Types, variants, error tags, effects, and type variables are `PascalCase`. A `TypeName` is never a value and an `Ident` is never a type; the resolver reports `V1004` on a mismatch.

## Comments and attributes [#comments-and-attributes]

```sakura
// A line comment. /* Block comments */ nest.

/// A doc comment. It attaches to the next item and is exported with its signature.
pub fn double(n: Int) -> Int {
  n * 2
}
```

`@name(args)` attributes precede items. The v0 set is `@tool`, `@llm`, `@external`, `@total`, `@contract_test`, `@deprecated`, `@server`, and `@test_only`. Attribute arguments are positional or `name: value`, as in `@llm(system: "…")` or `@contract_test(handlers: [Sql.memory()])`; this is the only place `name: value` appears in an argument list. `@trust("reason")` is the one attribute written as an expression, at a trust sink. An unknown attribute is `V1010`.

## Statements and newlines [#statements-and-newlines]

There are no mandatory semicolons. The lexer ends a statement at a line break only when **both** hold ([design §2.4](https://github.com/icantcodefyi/sakura-lang/blob/master/docs/design.md)):

1. The previous token can end an expression: a name, a hole, a literal, `)`, `]`, `}`, `>`, postfix `?`, `true`, `false`, `break`, `continue`, or `return`.
2. The next token cannot continue one: it is not `.`, `else`, `catch`, `)`, `]`, `}`, `,`, `=>`, `=`, `->`, `|`, `..`, or a binary operator other than `-`.

In practice:

- A line ending in an operator other than `>`, or in `(`, `[`, `{`, or `,`, continues. `>` ends a line so that `type Ids = List<Int>` terminates; write a continued comparison with `>` at the start of the next line.
- A line starting with `.`, `else`, or `catch` continues the previous one, so method chains and `} else {` work across lines.
- `foo` followed by `(bar)` on the next line is **two** statements. So is `foo` followed by `-1`.
- Newlines inside `(` and `[` are never significant. `;` is always accepted; `sakura fmt` removes redundant ones.

## Bindings [#bindings]

`let` binds an immutable name. `var` binds a name that can be reassigned. Local types are inferred.

```sakura
fn bindings() -> Int {
  let big = 20_000          // underscores between digits
  let mask = 0xFF           // also 0b1010
  let ratio = 0.75          // Float needs a digit on both sides of the dot
  let primes = [2, 3, 5, 7] // List<Int>
  let pair = (1, "one")     // tuple; pair.0, pair.1
  var count = 0
  count = count + List.len(primes)
  count + big + mask
}
```

`()` is the `Unit` value. There is no unary plus; `-5` is unary minus applied to `5`. Reassigning a `var` changes the binding, never a value seen elsewhere ([Memory model](/docs/tour/memory)). Top-level `let` declares a constant; a `pub let` needs a type annotation.

## Functions [#functions]

Every top-level function has a full signature: parameter types, `-> Result`, and an effect row unless it is pure.

```sakura
fn add(a: Int, b: Int) -> Int {
  a + b
}

fn clamp(n: Int, lo: Int, hi: Int) -> Int {
  if n < lo { return lo }
  if n > hi { return hi }
  n
}

pub fn greet(name: String) -> Unit ! {Console} {
  Console.print("hello, {name}")
}
```

- A block's value is its final expression. `return e` exits the enclosing function or lambda early.
- A signature without `!` is pure (`! {}`). Effects are covered in [Effects](/docs/tour/effects).
- `-> Unit` is written out; there is no default return type.
- Items are private to their module unless marked `pub`.
- A non-final expression statement whose value is not `Unit` is an error (`V2040`). Write `let _ = e` to discard on purpose.

## Control flow [#control-flow]

`if` is an expression. With `else`, both branches must have the same type. Without `else`, its type is `Unit`.

```sakura
fn label(score: Int) -> String {
  if score >= 90 { "excellent" } else if score >= 50 { "pass" } else { "keep going" }
}

fn can_vote(age: Int, registered: Bool, suspended: Bool) -> Bool {
  age >= 18 && registered && !suspended
}
```

`match` branches on the shape of a value. Arms end with a newline or `,`. Guards use `if`. A match must be exhaustive, and a literal pattern on `Int`, `String`, or `Float` needs a `_` arm. See [Types](/docs/tour/types#pattern-matching).

```sakura
fn describe(n: Option<Int>) -> String {
  match n {
    Some(0) => "zero"
    Some(x) if x > 0 => "positive"
    Some(_) => "negative"
    None => "missing"
  }
}
```

## Operators [#operators]

Tightest first ([design §3.1](https://github.com/icantcodefyi/sakura-lang/blob/master/docs/design.md)):

| Level | Operators | Associativity |
| --- | --- | --- |
| 1 | call `f(x)`, field `.x`, tuple index `.0`, index `[i]`, raise `?` | postfix |
| 2 | `-` `!` | prefix |
| 3 | `*` `/` `%` | left |
| 4 | `+` `-` | left |
| 5 | `..` (range, exclusive end) | none |
| 6 | `==` `!=` `<` `<=` `>` `>=` | none |
| 7 | `&&` | left |
| 8 | `\|\|` | left |

- Both operands must have the same type. `Int + Float` is a type error; convert with `Int.to_float` or `Float.round`.
- `+` also concatenates `String`. Nothing else is overloaded, and there are no user-defined operators.
- `Int` arithmetic is checked. Overflow (`D001`) and division by zero (`D002`) are defects, not errors.
- `a < b < c` is `V0010`. `&&` and `||` short-circuit.
- `xs[i]` is `List.get(xs, i)` and returns `Option`.

Four parsing rules remove guesswork ([design §3.2](https://github.com/icantcodefyi/sakura-lang/blob/master/docs/design.md)):

- In expressions, `<` is always comparison. There are no call-site type arguments: `List<Int>.empty()` and `f<T>(x)` are `V0011`. Name the type with a binding annotation instead: `let xs: List<Int> = List.empty()`, `let rows: List<User> = Sql.query(q)`.
- There are no named arguments: `f(name: v)` is `V0014`. Pass values positionally, or pass a record when there are many options (`Budget.limits(Limits { tokens: Some(20_000), usd: None, calls: None })`).
- In the header of `if`, `while`, `for`, `match`, and `with`, `Name {` starts the block, not a record literal. Parenthesize record literals there.
- `if`, `match`, `with`, `try`, `fn`, `handler`, and `fail` expressions are never operands without parentheses: `(match x { … }).len()`, not `match x { … }.len()` (`V0012`).

## Strings [#strings]

Strings are UTF-8 and may span lines. `{expr}` interpolates any expression whose type is `Show`. Escapes are `\\ \" \n \r \t \0 \{ \}` and `\u{…}`; write `\{` for a literal brace.

```sakura
fn inbox(name: String, unread: List<String>) -> String {
  "Hello, {name}. You have {List.len(unread)} messages. \{not interpolated\}"
}
```

Interpolating a `Secret<T>` is a compile error (`V2031`). Tagged strings `sql"…"`, `html"…"`, `url"…"`, and `shell"…"` pass interpolations to the tag as values and never splice them raw. See [Types](/docs/tour/types#tagged-templates).

## Lambdas and function types [#lambdas-and-function-types]

There is one lambda form: `fn(params) { body }`, with optional parameter and result annotations.

```sakura
fn twice<e>(f: fn(Int) -> Int ! e, x: Int) -> Int ! e {
  f(f(x))
}

test "twice applies f two times" {
  let add3 = fn(x) { x + 3 }
  let mul2 = fn(x: Int) -> Int { x * 2 }
  expect_eq(twice(add3, 3), 9)
  expect_eq(twice(mul2, 3), 12)
}
```

A function type is `fn(A, B) -> C ! row`; without `!` it is pure. The lowercase `e` is an effect-row variable, so `twice` performs whatever `f` performs. Lambdas capture values, not variables, and cannot assign to an outer `var` (`V2041`). A local lambda cannot call itself (`V1020`); lift it to a top-level function.

## Method-call syntax [#method-call-syntax]

`x.f(a)` means `T.f(x, a)`, where `T` is the type of `x` and `f` is looked up in `T`'s home module. So `xs.map(f)` is `List.map(xs, f)` and `s.len()` is `String.len(s)`. There are no methods, no `self`, and no pipe operator. Chains continue across lines with a leading `.`:

```sakura
type User = { name: String, active: Bool }

fn names(users: List<User>) -> List<String> {
  users
    .filter(fn(u) { u.active })
    .map(fn(u) { u.name })
}
```

The full lookup order is in [Types](/docs/tour/types#method-call-resolution).

## Loops [#loops]

`for pattern in expr { }` iterates a `List`, `Range`, `Set`, or `Map` (as `(key, value)` pairs). `while cond { }` loops while a condition holds. `break` and `continue` work in both. Loops are statements and produce no value; accumulate in a `var`, or use `List.fold`.

```sakura
fn total(xs: List<Int>) -> Int {
  var sum = 0
  for x in xs {
    if x < 0 { continue }
    sum = sum + x
  }
  sum
}

fn total_fold(xs: List<Int>) -> Int {
  List.fold(xs, 0, fn(acc, x) { if x < 0 { acc } else { acc + x } })
}

fn countdown(n: Int) -> Unit ! {Console} {
  for i in 0..n {
    Console.print("{n - i}")
  }
}
```

## Holes [#holes]

`?name` is a typed hole: an unfinished expression. `sakura check` accepts holes and reports each one's expected type, the locals in scope, and ranked candidates. `sakura build` and `sakura run` reject them (`V2050`). See [Typed holes](/docs/agents/typed-holes).

```sakura
fn unfinished(x: Int) -> Int { ?result }
```

## Modules [#modules]

One file is one module. The module path is the file path under the package's `src/`: `src/users/repo.sakura` is module `users/repo`. The first import segment is a package name (`std`, the current package, or a dependency in `sakura.toml`).

```sakura
import std/http.{Http, Request, Response}    // bind names directly
import std/sql                               // bind only the module name `sql`
import acme/billing/invoices as inv          // rename
```

- Nothing outside the prelude is in scope implicitly. `import std/sql` binds `sql` and nothing else, so a row that mentions `Sql` needs `import std/sql.{Sql}`. On an unresolved std name, `V1002` offers the import as a machine-applicable fix.
- No wildcard imports, no relative imports, no re-exports, and no import cycles (`V1030`).
- Variants and error tags live directly in their module's namespace (`Some`, `EmailTaken`); `Option.Some` is also accepted.
- `pub type` exports a type with its constructors and fields. `pub opaque type` exports only the name: other modules can use its values but not construct or destructure them (`V1006`).
- The prelude needs no import: the built-in types (`Int`, `String`, `List`, `Map`, `Set`, `Secret`, `Untrusted`, …), `Option`, `Result`, `Duration`, `Instant`, `Uuid`, `Json`, `Level`, the constructors `Some None Ok Err`, the effects `Fail Console Clock Random Env Log Task Test`, `show` and the `expect…` test functions, and the tags `sql html url shell`.
- The entry point is `pub fn main() -> Unit ! {…}` in `src/main.sakura`.

## What the compiler checks [#what-the-compiler-checks]

- Every top-level function has a complete signature (`V3001` lists effects a body performs beyond it).
- Casing matches the namespace (`V1004`). Reserved words cannot be identifiers (`V0007`).
- Unused non-`Unit` results are errors (`V2040`).
- Name conflicts within a module are errors (`V1001`). A local shadowing an import is a warning (`W1002`).
- Every `match` is exhaustive (`V4001`). Unreachable arms are reported (`V4002`).
- `sakura fmt` has no options: 2-space indent, width 100, one canonical layout.

## Next [#next]

[Types](/docs/tour/types): records, variants, refinements, generics, and why there is no null.
