# Laila Lang: A Pseudo-Language for Technical Writing

The complete language specification for Laila Lang (La3), the pseudo-language used in code examples across this blog. Laila Lang draws on Rust, C, TypeScript, and Lua to produce notation that is unambiguous, readable, and consistent regardless of which real language the reader knows best. This document specifies its lexical structure, type system, semantics, and standard library.

Published: August 1, 2026
Canonical HTML: https://en.glazastov.com/laila-lang-reference
Markdown: https://en.glazastov.com/laila-lang-reference.md
Signature verification: server-verified valid
PGP fingerprint: 9D50 A8CF A03B 1B77 05B5 D6CA 90A7 15BE E539 6D0D
Signed source: https://en.glazastov.com/_signed/src/posts/laila-lang-reference.md
Detached signature: https://en.glazastov.com/_signed/src/posts/laila-lang-reference.md.asc
Public key: https://en.glazastov.com/pgp.asc

Every technical blog eventually faces the same problem. Code examples written in Python read strangely to systems programmers; Go examples look alien to TypeScript developers; C examples require a mental translation step for anyone who has spent the last five years in managed languages. The result is friction at precisely the moment when the text needs the reader's full attention.

Laila Lang is a pseudo-language designed to solve this. It is not intended to be compiled or executed; it is intended to be *read*. Its syntax is assembled from the parts of four real languages that contribute the most clarity per character, namely **Rust**, **C**, **TypeScript**, and **Lua**. No piece of Laila Lang syntax is arbitrary; each choice has a source and a reason.

This document is the complete specification. It defines the lexical structure, the type system, the evaluation semantics, the standard library, and a reference grammar. Where a construct has a deliberately defined behavior (for example the result type of an operator, or whether two values compare equal), that behavior is stated explicitly so that examples across the blog never depend on the reader guessing.

## 0. Design Principles

Laila Lang optimizes for one thing, reading clarity for an audience that knows *some* mainstream language but not necessarily the same one. Five principles follow from that goal.

**Immutability by default.** Variables are immutable unless explicitly declared mutable. This is Rust's contribution, and it matters; when you see `let x = 42`, you know `x` will not change. When you see `let mut x = 42`, the mutation is a signal, not a surprise.

**Expressions over statements.** Most constructs that produce a value can appear anywhere a value is expected. `if` is an expression, `match` is an expression, and a block's last expression is its value without a `return` keyword. This reduces syntactic noise.

**Explicit types where they carry information, inferred where they do not.** Laila Lang has full type inference. Writing `let n: u16 = port` conveys that `port` is bounded and unsigned; writing `let n = 42` conveys that the type is obvious. Both are valid.

**One obvious way.** Where two notations would mean the same thing, Laila Lang keeps the one most readers already recognize and drops the other. There is one string-concatenation operator (`+`), one length query (`.len()`), and one null-like value (`nil`). Redundancy is noise, and noise is the enemy of a reading language. The one deliberate exception is absence, which has both a lightweight spelling (`T | nil`) and an explicit one (`Option<T>`); the exception is admitted precisely because the two are *one underlying value* viewed two ways (Section 2), not two competing concepts, and because signatures benefit from the louder form while locals benefit from the quieter one.

**Absence is explicit.** Laila Lang has exactly one null-like value, `nil`, borrowed from Lua. There is no `null` and no `undefined`. Absence is expressed either as a bare optional (`T | nil`) for lightweight cases or as `Option<T>` when a function signature should advertise that a value may be missing. Recoverable failure is a value, `Result<T>`, never a silently discarded code.

### Why a pseudo-language at all

A fair objection is that the world does not need another language, not even a fake one. Why not write every example in Python, or in Rust, and let readers translate?

The answer is that translation is exactly the cost we are trying to remove. A reader fluent in Go who meets a Python decorator, or a Python developer who meets Rust lifetimes, spends part of their attention decoding syntax that is incidental to the point being made. Multiply that across a long post and the incidental load competes with the real idea. A pseudo-language pays a small fixed cost (the reader learns one short dialect, this document) in exchange for removing the per-example translation tax for the rest of the blog.

The design target is *clarity per character*. Every token should either carry meaning the reader needs or be invisible. That is why the syntax is borrowed rather than invented; borrowed notation is already familiar to some part of the audience, and familiarity is readability. The four sources were chosen because each is the clearest known expression of a specific idea.

- **Rust** contributes the safety-and-intent vocabulary (`let`/`mut`, `match`, `Result`, references, enums with data) because it makes a program's intent visible in its source rather than hidden in convention.
- **C** contributes the low-level vocabulary (raw pointers, fixed-size arrays, bit operations) because posts about how machines actually work must be able to talk about memory as it physically is.
- **TypeScript** contributes union types, structural-style interface declarations, and `async`/`await` because those are the most widely recognized modern forms of those ideas. (Interface *conformance* is still explicit rather than structural, see Section 9.)
- **Lua** contributes a few small, high-clarity touches (`nil`, multiple return values, floor division) because they read cleanly and carry almost no baggage.

Where two sources offered the same idea, the version most readers already recognize won, and the other was dropped to keep the dialect small. The guiding rule for the rest of this document is simple. When two designs are equally expressive, the more readable one wins; when readability ties, the one that prevents a silent mistake wins.

## 1. Lexical Structure

### Comments

```la3
// Single-line comment.

/*
   Multi-line comment.
   Used for longer explanations or temporarily disabling code.
*/
```

Documentation comments use `///` and support Markdown.

```la3
/// Parses a port number from a string.
/// Returns `Err` if the string is not a valid integer in `[1, 65535]`.
fn parse_port(s: str) -> Result<u16> {... }
```

### Tokens and Whitespace

Source text is UTF-8. Whitespace separates tokens and is otherwise insignificant, except that a newline can terminate a statement (see Semicolons). Tokens are keywords, identifiers, literals, operators, and delimiters. The lexer is greedy; it always consumes the longest valid token.

### Semicolons

Semicolons are optional. A newline ends a statement when the text so far already forms a complete expression; if the line ends mid-expression (for example on an operator or an open bracket) the statement continues onto the next line. Use a semicolon when you put multiple expressions on one line, or when the grammar would otherwise be ambiguous.

```la3
let x = 1
let y = 2

let a = 1; let b = 2    // two bindings on one line
```

### Keywords

The following identifiers are reserved and cannot be used as names.

```la3
let  mut  const  fn  return  if  else  match  loop  while  for  in
break  continue  struct  enum  interface  impl  for  type  mod  use  pub
async  await  move  spawn  try  catch  finally  unsafe  as  self  Self  true  false  nil
```

### Identifiers and Casing Conventions

| Kind | Convention | Examples |
| ---- | ---------- | ------- |
| Variable, function, module | `snake_case` | `retry_count`, `parse_frame`, `net::http` |
| Type, struct, enum, interface | `PascalCase` | `TcpStream`, `ParseError`, `Shape` |
| Constant | `SCREAMING_SNAKE_CASE` | `MAX_RETRIES`, `DEFAULT_PORT` |

### Literals

```la3
42            // integer, defaults to i32
42u8          // integer with type suffix
0xFF  0o17  0b1010   // hex, octal, binary
1_000_000     // underscores as digit separators
3.14   1.0e-9   2.5f32   // float, f64 by default
true   false  // bool
'a'  '€'  '😀'   // char, exactly one Unicode scalar value
"hello"       // str
f"x={x}"      // formatted str (see Section 13)
nil           // the absence value
```

A `char` literal contains exactly one Unicode scalar value. There is no empty char literal `''`; the empty case is represented with `Option<char>` or an empty `str`.

### Design rationale

**Optional semicolons.** A statement ends at a newline, so beginners can write one statement per line and never think about terminators. This is safe (and free of the subtle bugs that automatic semicolon insertion causes in some languages) because Laila Lang ends a statement at a newline only when the text so far already forms a complete expression; if a line ends with an operator or an open bracket, the statement continues onto the next line. Advanced readers will recognize this as the same rule that makes newline handling predictable in Go and Swift, which is why explicit semicolons are needed only to put two statements on one line or to resolve genuinely ambiguous grammar.

**Why not significant whitespace.** Indentation-as-syntax reads cleanly for simple code but becomes ambiguous in the dense, deeply nested snippets common in systems writing (a closure inside a `match` inside a loop). Braces make nesting unambiguous on the page, and on the page is where this language lives.

**Casing as a free namespace.** `PascalCase` for types and `snake_case` for values let a reader tell what kind of thing a bare identifier is with no surrounding context. Seeing `Frame` you know it is a type; seeing `frame` you know it is a value. The compiler does not need this, but the reader does, and reader cost is what this language minimizes. `SCREAMING_SNAKE_CASE` carries the same instant signal for "this never changes."

## 2. Types

### Primitive Types

| Type | Description | Literal examples |
| ---- | ----------- | --------------- |
| `bool` | Boolean | `true`, `false` |
| `i8`, `i16`, `i32`, `i64` | Signed integers (8 to 64 bit) | `-1`, `2147483647` |
| `u8`, `u16`, `u32`, `u64` | Unsigned integers (8 to 64 bit) | `0`, `255`, `65535` |
| `isize`, `usize` | Platform-pointer-sized signed/unsigned | `-1`, `1024` |
| `f32`, `f64` | IEEE 754 floating point | `3.14`, `1.0e-9` |
| `char` | Unicode scalar value | `'a'`, `'€'`, `'😀'` |
| `str` | UTF-8 string slice | `"hello"` |
| `byte` | Alias for `u8` | `0xFF`, `42` |
| `nil` | The absence value; its own type | `nil` |
| `()` | Unit, the empty tuple | `()` |
| `!` | Never, the type of expressions that do not return | (no literal) |

When a numeric literal has no annotation, `i32` and `f64` are the defaults. Type suffixes are available when needed, for example `42u8` and `3.14f32`.

### Compound Types

```la3
// Tuple, fixed length, heterogeneous
let pair: (str, i32) = ("alice", 30)

// Fixed-size array, homogeneous, size is part of the type (from C/Rust)
let buf: [u8; 16] = [0; 16]    // 16 zero bytes

// Slice, a borrowed view into a contiguous sequence, does not own its data
let window: &[i32] = &arr[1..4]

// Growable list, heap-allocated, variable length (analogous to Rust's Vec<T>)
let xs: List<i32> = [1, 2, 3]

// Map (analogous to Rust's HashMap<K, V>)
let m: Map<str, i32> = { "alpha": 1, "beta": 2 }

// Set
let s: Set<str> = { "a", "b" }
```

The three sequence forms are deliberately distinct in type notation and in meaning. The value literal `[1, 2, 3]` is shared between them and resolves to the right form from the binding's type (it builds a `List<T>` by default and coerces to `[T; N]` when the type asks for it).

| Form | Meaning | Owns data | Resizable |
| ---- | ------- | --------- | --------- |
| `[T; N]` | Fixed-size array, length `N` in the type | yes | no |
| `&[T]` | Slice, a borrowed view | no | no |
| `List<T>` | Growable list on the heap | yes | yes |

A list literal `[a, b, c]` constructs a `List<T>` by default and coerces to `[T; N]` when the binding's type requires it.

### Optionals, `nil`, and `Option<T>`

Laila Lang offers two ways to express absence, and they share a single underlying value.

A **bare optional** is a union with `nil`, written `T | nil`. It is the lightweight form, used for local variables and fields where the absence case is obvious from context. The `??`, `?.`, and narrowing operators (Section 4) work directly on bare optionals.

```la3
let port: u16 | nil = nil
let host: str | nil = lookup(name)
```

An **`Option<T>`** is the explicit form. It is an enum with two variants, `Some(T)` and `None`. Use it in function signatures and data structures where the possibility of absence should be advertised by the type itself, and where the method API (`map`, `unwrap_or`, `?`) is convenient.

```la3
fn first<T>(xs: &[T]) -> Option<T> {
    if xs.len() == 0 { None } else { Some(xs[0]) }
}
```

The relationship between the two forms is defined exactly, so examples never depend on guessing.

- At the value level there are only two cases, present or absent. The absent case is the single value `nil`. `None` is the name that the `Option<T>` enum gives to that same absent value; `None` and `nil` are equal (`None == nil` is `true`).
- The present case is `x` itself for a bare optional, and `Some(x)` for an `Option<T>`. `Some(x) == Some(y)` is `true` exactly when `x == y`.
- A bare `nil` literal has type `nil`, which unifies into any `T | nil` and into any `Option<T>` (as `None`). Conversion between `T | nil` and `Option<T>` is implicit at binding and call sites; the two are different presentations of the same data, not different data.

In short, `nil` is the value, `None` is the typed name `Option<T>` gives that value, and `Some`/the bare present value carry the contents.

### Union Types (from TypeScript)

```la3
fn accepts(value: str | i64 | bool) {... }
```

A union type is satisfied by any value whose type is listed in the union. The compiler requires narrowing before the value is used as a specific member.

```la3
fn format(v: str | i64) -> str {
    match v {
        s: str  => s,
        n: i64  => str(n),
    }
}
```

`T | nil` is the special case of a union with the `nil` type, and the optional operators (`??`, `?.`) are defined on exactly that case.

### Type Aliases

```la3
type Addr    = str
type Port    = u16
type Payload = List<u8>
type Handler = fn(Request) -> async Result<Response>
```

### Type Inference Rules

1. A binding without an annotation takes the type of its initializer.
2. An unsuffixed integer literal defaults to `i32`; an unsuffixed float literal defaults to `f64`.
3. A list literal infers `List<T>` where `T` is the unified type of its elements, unless the context demands `[T; N]` or `&[T]`.
4. There is no implicit numeric widening or narrowing; mixed-type arithmetic requires an explicit `as` cast (Section 4).
5. Generic type parameters are inferred from argument types at the call site when unambiguous, and may be supplied explicitly with the turbofish `::<T>`.

### Design rationale

**The full integer zoo.** A managed-language reader may wonder why there are ten integer types. The reason is that a large fraction of this blog is about systems, protocols, and cryptography, where the width of a number is part of its meaning. A TLS record length field is a `u16` because the protocol says so, and writing it as a generic `int` would erase information the post is specifically about. `usize` and `isize` are kept separate because they are pointer-sized; their width depends on the target machine, and folding them into `u64` would hide that dependency. For posts where width does not matter, inference and the `i32`/`f64` defaults keep the noise away.

**Why `i32` and `f64` are the defaults.** They are the types most readers expect an unannotated number to be, matching Rust, C's `int`, and the IEEE double. A default should surprise no one.

**Why no implicit numeric conversion.** Many languages silently widen or narrow numbers, which is convenient right up until a `u16` length wraps around or a large `i64` quietly loses precision as `f32`. Requiring an explicit `as` makes every such conversion visible at the exact point it happens, which is both safer and, for a teaching text, more honest about what the hardware does. The cost is a few extra `as` tokens, a fair price in prose meant to be studied.

**Why two ways to say "absent."** This is the most debated choice, so the reasoning deserves to be plain. A bare optional (`T | nil`) is the lightest possible notation for "maybe missing," ideal for a local where the reader can see the whole story at once. But a signature returning `str | nil` advertises absence less loudly than one returning `Option<str>`, and signatures are read far more often than they are written. So both exist, the bare form for ergonomics and `Option<T>` for self-documenting APIs. The decisive choice is that they are the same value underneath (see above), so there is no conversion ceremony and no second kind of null. This sidesteps what Tony Hoare called his "billion dollar mistake," an absent value the type system never forces you to consider, by keeping absence visible in the type while still feeling lightweight.

**Why unions need narrowing.** A union `str | i64` says a value is one of several types but not which, so using it as a specific type before checking would be a category error. Requiring a `match` (or an `if let`) to narrow is what makes unions safe; it is the discipline a careful reader applies mentally, made explicit and checked by the compiler.

### Side by side

Absence, from Lua's `nil`.

```lua
-- Lua
local host = lookup(name)        -- nil if not found
if host == nil then host = "localhost" end
```

```la3
// La3
let host = lookup(name) ?? "localhost"    // host: str
```

Lua gives a single `nil`. La3 keeps that one absence value and adds a type-level wrapper (`Option<T>`) plus the `??` operator for the default, so absence can be either lightweight or advertised in a signature.

Union types, from TypeScript.

```ts
// TypeScript
function format(v: string | number): string {
  return typeof v === "string" ? v : String(v);
}
```

```la3
// La3
fn format(v: str | i64) -> str {
    match v {
        s: str => s,
        n: i64 => str(n),
    }
}
```

Both spell the union `A | B`. La3 narrows with `match` instead of `typeof`, and the compiler checks that the narrowing is exhaustive.

`Option<T>`, from Rust.

```rust
// Rust
fn first<T: Clone>(xs: &[T]) -> Option<T> {
    if xs.is_empty() { None } else { Some(xs[0].clone()) }
}
```

```la3
// La3
fn first<T>(xs: &[T]) -> Option<T> {
    if xs.len() == 0 { None } else { Some(xs[0]) }
}
```

Nearly identical. La3 leaves the `Clone` bound off the surface because example code does not dwell on ownership unless the post is specifically about it.

## 3. Variables

Variables are immutable by default. This is Rust's rule, deliberately kept.

```la3
let x = 42           // immutable; type inferred as i32
let mut y = 10       // mutable
y = y + 1            // allowed: y is declared mut

const MAX: u32 = 65535    // compile-time constant; type annotation required
```

Type annotations follow the identifier with `:`.

```la3
let port: u16 = 8080
let ratio: f64 = successful as f64 / total as f64
```

**Shadowing** rebinds the same name, optionally to a different type. The old binding is gone; there is no mutation.

```la3
let input = read_line()            // str
let input = input.trim()           // str, shadowed by trimmed version
let input = input.parse::<i64>()?  // now i64
```

**Destructuring** unpacks compound values in a binding.

```la3
let (a, b)    = (1, 2)
let (ok, err) = try_operation()

// Struct destructuring
let Point { x, y } = origin

// List head/tail (spread)
let [head,..tail] = items
```

**Casting** uses the `as` keyword for numeric conversions.

```la3
let n: i64 = 300
let b: u8  = n as u8    // truncates to 44
let f: f64 = n as f64   // lossless
```

### Scoping and Lifetime

Bindings are block-scoped and live from their declaration to the end of the enclosing block. A binding may be shadowed by a later `let` in the same or a nested block. References (Section 11) may not outlive the value they point to, a rule the compiler enforces.

### Design rationale

**Immutability by default.** When a binding cannot change, a reader can hold its value in mind for the rest of the scope without rechecking, which removes a whole class of "where did this get modified" questions. That is why `let` is immutable and `mut` is the marked, visible exception. In concurrent code the same guarantee is what makes sharing safe; an immutable value can be read from many threads with no coordination. The cost is one keyword on the genuinely mutable bindings, which doubles as documentation.

**Shadowing is not mutation.** Rebinding a name with a new `let` creates a fresh binding; the old value still exists for anything that captured it. This is why a parse pipeline (`read_line`, then `trim`, then `parse`) is allowed without `mut`, the reader sees a value transformed through stages, each stage its own immutable binding, rather than one mutable variable changing type underfoot. The distinction matters to advanced readers because a closure that captured the earlier binding keeps the earlier value.

**Why `as` for casts.** The same reasoning as the no-implicit-conversion rule; a cast can lose data through truncation, sign change, or lost precision, so it is spelled out where it happens. `n as u8` truncating `300` to `44` is surprising only when it is invisible; written explicitly, it is a documented choice.

### Side by side

Immutability and shadowing, from Rust.

```rust
// Rust
let x = 42;
let mut y = 10;
y += 1;
let input = read_line();
let input = input.trim();    // shadowing, a new binding
```

```la3
// La3
let x = 42
let mut y = 10
y += 1
let input = read_line()
let input = input.trim()     // shadowing, a new binding
```

The model is Rust's verbatim; La3 only drops the semicolons.

## 4. Operators

### Arithmetic

| Operator | Meaning | Notes |
| -------- | ------- | ----- |
| `+` `-` `*` | Add, subtract, multiply | Integer or float; operands must share a type |
| `/` | Division | Integer division for integer operands (truncates toward zero) |
| `%` | Remainder | Sign matches the left operand |
| `**` | Exponentiation | Always produces `f64`; cast the result when an integer is needed, for example `(2 ** 10) as i32` |
| `//` | Floor division | Rounds toward negative infinity (from Lua) |

Mixed-type arithmetic requires an explicit cast.

```la3
let x: i32 = 7
let y: f64 = x as f64 / 3.0    // 2.333...
```

### Comparison

`==`, `!=`, `<`, `>`, `<=`, `>=` produce `bool`. Equality on primitives compares by value; equality on references compares the pointed-to values; equality on user types dispatches to a custom `eq` method when defined. `nil == nil` is `true`, `nil == None` is `true`, and `Some(a) == Some(b)` is `a == b`.

### Logical

`&&`, `||`, `!` are short-circuit and always produce `bool`. `&&` evaluates its right operand only when the left is `true`; `||` evaluates its right operand only when the left is `false`. Unlike Lua, they do not return one of their operands; the result is always boolean.

```la3
if connected && !buffer.is_empty() { flush(buffer) }
let valid = port > 0 && port <= 65535
```

### Optional Operators

Two operators act on bare optionals (`T | nil`). They make the common "use a value if present, otherwise fall back or stop" patterns explicit without the Lua trick of overloading `&&`/`||`.

The **null-coalescing** operator `??` returns its left operand when it is non-`nil`, otherwise its right operand.

```la3
// opts.port: u16 | nil
let port = opts.port ?? 443     // u16: opts.port if present, else 443
```

The **optional-chaining** operator `?.` accesses a field or method only when the receiver is non-`nil`, and short-circuits to `nil` otherwise.

```la3
// user: User | nil
let name = user?.name           // str | nil: nil if user is nil, else user.name
let city = user?.address?.city  // chains; nil if any link is nil
```

These operate on bare optionals (`T | nil`), not on `Option<T>` wrappers. For `Option<T>`, use `map`, `unwrap_or`, and the `?` operator (Section 8).

### Bitwise

`&`, `|`, `^`, `~`, `<<`, `>>` operate on integer types. `~` is bitwise NOT (from C), and `^` is XOR.

```la3
let flags: u8 = 0b0000_1101
let masked = flags & 0b0000_1111
let shifted = flags >> 2
```

### Ranges and String Concatenation

`..` and `..=` build ranges, `0..n` (end exclusive) and `0..=n` (end inclusive). The same `..` token, in a struct literal, is the struct-update spread `..other` (Section 6). These two uses never appear in the same syntactic position, so there is no ambiguity.

String concatenation uses `+`. There is exactly one concatenation operator.

```la3
let s = "Hello, " + name + "!"
```

### Length

The length of a string (in bytes), list, array, map, or set is `.len()`. There is exactly one length query.

```la3
let n = items.len()
let empty = items.len() == 0    // or items.is_empty()
```

### Reference and Dereference

These appear in the memory model sections.

```la3
let r = &x       // take a reference to x
let v = *r       // dereference r to get its value
```

### Operator Precedence

Highest to lowest, following Rust and C conventions. Operators in the same row share precedence and associate left to right unless noted.

1. Postfix `.field`, `?.`, method call, indexing `[]`, `?` (try)
2. Unary prefix `!`, `-`, `~`, `*`, `&`
3. `**` (right associative)
4. `*`, `/`, `%`, `//`
5. `+`, `-`
6. `<<`, `>>`
7. `&`
8. `^`
9. `|`
10. `..`, `..=` (range)
11. `==`, `!=`, `<`, `>`, `<=`, `>=`
12. `&&`
13. `||`
14. `??`
15. `as`
16. Assignment `=`, `+=`, `-=`, and the other compound assignments (right associative)

### Design rationale

**Logical operators return `bool`, and why this changed.** Lua's `and`/`or` return one of their operands, so `a or b` yields `a` when `a` is truthy. That is elegant in Lua, but it is a trap in a language that otherwise looks like Rust and TypeScript, because a reader from those languages sees `user && user.name` and expects a boolean, not the value of `user.name`. Familiar-looking syntax that means something subtly different is the worst possible outcome for a reading language, so `&&`, `||`, and `!` always produce `bool` here, exactly as in C, Rust, Java, and TypeScript. The genuinely useful parts of the Lua idiom, "use this value if present, otherwise a default" and "reach inside only if it exists," are given their own unambiguous operators, `??` and `?.`, taken from TypeScript where they already mean precisely that.

**`??` and `?.` operate on bare optionals.** `x ?? d` means "x if present, else d," and `a?.b` means "b if a is present, else nil." Both short-circuit, so the fallback and the access are evaluated only when needed. They are defined on `T | nil` rather than on `Option<T>` because they are the lightweight counterpart to the lightweight optional; the `Option<T>` wrapper has its own method vocabulary (`unwrap_or`, `map`, `?`) for the same jobs, and keeping the two families separate means the question "does `??` work on an `Option`" never arises.

**Why `**` always yields `f64`.** Exponentiation naturally produces non-integers (`2 ** -1` is `0.5`, `2 ** 0.5` is irrational), and integer exponentiation overflows almost immediately. A type-preserving `**` would have to answer "what is the type of `2 ** -1`" with either a runtime error or a silent change of meaning, both worse than one clear rule. So `**` produces `f64` always, and the rare integer power is written `(2 ** 10) as i32`, which is explicit about rounding back to an integer. The cost is one cast in a minority of cases; the benefit is that the type of `a ** b` never depends on the values of `a` and `b`.

**Why both `/` and `//` exist.** Integer `/` truncates toward zero, which is what C and Rust do and what most readers expect. Floor division `//` rounds toward negative infinity, which is what you want when bucketing a possibly-negative index into fixed-size pages. They genuinely differ for negative operands (`-7 / 2` is `-3`, `-7 // 2` is `-4`), so both are kept; this is one of the few places the language carries two operators, because they do different things rather than the same thing two ways.

**One concatenation operator, one length query.** An earlier draft let you concatenate with both `+` and Lua's `..`, and take a length with both `#x` and `.len()`. That is two ways to do each of two things, pure noise in a language whose whole purpose is reading clarity. `+` wins for concatenation because nearly every reader already reads `+` on strings as "join," which frees `..` to mean only ranges and struct spread. `.len()` wins for length because it is self-describing and discoverable (a reader can guess `.len()` exists; nobody guesses `#`), and three extra characters cost nothing in prose meant to be read slowly.

**Why the precedence table looks the way it does.** It follows the conventions a reader already carries from mathematics and from C/Rust, so that `a + b * c` and `a == b && c` parse the way intuition says. `**` is right-associative and sits just below the prefix operators so that `2 ** 3 ** 2` is `2 ** 9`, matching mathematical convention. `??` sits low, below comparison and the boolean operators, because it is usually the last step (compute something that might be `nil`, then supply a default). `as` sits lowest of the binary operators so that `n as f64 / 2.0` casts before dividing, which is almost always the intent.

**Why `~` and `!` are different.** C readers expect `~` for bitwise NOT and `!` for logical NOT, and keeping them distinct means `~flags` (flip every bit) and `!done` (negate a boolean) are never confused, which a single `!` for both would invite.

### Side by side

The logical-operator difference, Lua versus La3. This is the one place La3 deliberately departs from a source.

```lua
-- Lua: `and`/`or` return an operand, not a bool
local name = user and user.name   -- user.name, or nil/false
local port = opts.port or 443     -- opts.port, or 443
```

```la3
// La3: && and || are strictly bool; the value idioms get their own operators
let name = user?.name          // str | nil
let port = opts.port ?? 443    // u16
```

Lua overloads `and`/`or` to also do optional access and defaulting. La3 splits those into `?.` and `??` so that `&&`/`||` always mean "boolean," which is what Rust and TypeScript readers expect.

Floor division, from Lua.

```lua
-- Lua
print(-7 // 2)   -- -4
```

```la3
// La3
io.println(str(-7 // 2))   // -4   (// floors; / would give -3)
```

Optional chaining and coalescing, from TypeScript.

```ts
// TypeScript
const city = user?.address?.city ?? "unknown";
```

```la3
// La3
let city = user?.address?.city ?? "unknown"
```

Identical; these two operators are taken from TypeScript unchanged.

## 5. Functions

### Basic Declaration

```la3
fn add(a: i32, b: i32) -> i32 {
    a + b    // last expression is the return value, no semicolon
}

fn clamp(x: f64, lo: f64, hi: f64) -> f64 {
    if x < lo { return lo }    // early return is fine
    if x > hi { return hi }
    x
}

// No return value; the return type is () (unit)
fn log(msg: str) {
    io.println("[LOG] " + msg)
}
```

### Multiple Return Values (from Lua)

A function can return a tuple, and the caller destructures it.

```la3
fn divmod(a: i64, b: i64) -> (i64, i64) {
    (a / b, a % b)
}

let (quotient, remainder) = divmod(17, 5)
```

### Generics (from Rust/TypeScript)

Type parameters go after the function name, constrained by interfaces with `:`.

```la3
fn max<T: Ord>(a: T, b: T) -> T {
    if a >= b { a } else { b }
}

fn first<T>(xs: &[T]) -> Option<T> {
    if xs.len() == 0 { None } else { Some(xs[0]) }
}

fn zip<A, B>(xs: List<A>, ys: List<B>) -> List<(A, B)> {
    let len = min(xs.len(), ys.len())
    (0..len).map(|i| (xs[i], ys[i]))
}
```

### Variadic Parameters

```la3
fn sum(...args: i32) -> i32 {
    args.reduce(0, |acc, x| acc + x)
}

let total = sum(1, 2, 3, 4, 5)    // 15
```

### Closures (from Rust)

Closures capture variables from the enclosing scope. Types are inferred from usage. The syntax uses `|` pipes.

```la3
let double  = |x| x * 2
let add     = |a, b| a + b
let verbose = |name: str| -> str { "Hello, " + name }
```

Closures capture by reference by default. `move` transfers ownership into the closure, which is necessary when the closure outlives the current scope, for example when it is passed to a new thread.

```la3
let threshold = 100
let exceeds   = |x| x > threshold      // borrows threshold

let base   = compute_base()
let scaled = move |x| x * base         // owns base; base is invalid here after
```

Higher-order collection methods.

```la3
let evens    = [1..=10].filter(|x| x % 2 == 0)
let squares  = evens.map(|x| x * x)
let total    = squares.reduce(0, |acc, x| acc + x)
let sorted   = records.sort_by(|a, b| a.timestamp < b.timestamp)
let grouped  = entries.group_by(|e| e.category)
```

### Async Functions (from TypeScript)

An `async fn` returns a future. `await` suspends until the future resolves.

```la3
async fn fetch_json<T>(url: str) -> Result<T> {
    let resp = await http.get(url)
    resp.json::<T>()
}

async fn main() {
    match await fetch_json::<Config>("https://config.example.com/v1") {
        Ok(cfg)  => apply(cfg),
        Err(e)   => io.println("config unavailable: " + e),
    }
}
```

Multiple futures can be awaited together; `await all(...)` resolves when all complete (Section 12).

```la3
async fn check_all(hosts: List<str>) -> Map<str, bool> {
    let futures = hosts.map(|h| ping(h))
    let results = await all(futures)
    hosts.zip(results).collect()
}
```

### The `?` Operator

`?` propagates failure. Applied to a `Result`, it returns `Err(e)` from the enclosing function when the value is `Err(e)`, and evaluates to `v` when it is `Ok(v)`. Applied to an `Option`, it returns `None` on `None` and evaluates to `v` on `Some(v)`. It may appear only inside a function whose return type is a matching `Result` or `Option` (Section 8).

### Design rationale

**Blocks are expressions.** The last expression of a block is its value, so `let x = if cond { a } else { b }` needs no temporary and no assignment in each branch. Beginners can ignore this and still write `return`; advanced readers get composability, since `match`, `if`, and `{}` can appear wherever a value is wanted. `return` still exists for early exit, which is clearer than forcing every guard clause into an expression.

**Tuples for multiple returns.** Returning `(quotient, remainder)` and destructuring at the call site is clearer than C-style out-parameters (which hide outputs in the argument list) or forcing a named struct for every multi-value return. The caller sees exactly how many values come back and names them on the spot.

**Closures capture by reference, `move` to own.** By default a closure borrows what it uses, which is cheap and is what you want for a closure passed to `map` and used immediately. But a closure that outlives the current scope (handed to a new thread, or stored for later) cannot safely borrow locals that are about to disappear, so `move` transfers ownership into it. Making `move` explicit means the reader can see, right at the closure, whether it borrowed or took ownership, which is exactly the information needed to reason about lifetimes and threads.

**`async fn` returns a future.** An async function does not run to completion when called; it returns a future that makes progress only when awaited. This models cooperative concurrency honestly (Section 12), and it is why `await` is visible at every suspension point rather than hidden.

**The `?` operator.** Propagating errors by hand (matching every fallible call and returning the `Err`) buries the happy path under boilerplate. `?` collapses that to one character at the call site, so the reader sees the normal flow with failure handling marked but not dominant. It is restricted to functions returning `Result` or `Option` because that is the only context where "return the error from here" has a meaning.

### Side by side

Implicit return and `?`, from Rust.

```rust
// Rust
fn read_config(path: &str) -> Result<Config, Error> {
    let text = fs::read_to_string(path)?;
    let cfg = serde_json::from_str(&text)?;
    Ok(cfg)
}
```

```la3
// La3
fn read_config(path: str) -> Result<Config> {
    let text = fs.read(path)?
    let cfg  = json.decode::<Config>(text)?
    Ok(cfg)
}
```

Same `?` propagation and same trailing-expression return. La3 fixes the error side into `Result<T>` (the error is always a message) so signatures stay short.

Multiple return values, from Lua.

```lua
-- Lua
local function divmod(a, b) return a // b, a % b end
local q, r = divmod(17, 5)
```

```la3
// La3
fn divmod(a: i64, b: i64) -> (i64, i64) { (a // b, a % b) }
let (q, r) = divmod(17, 5)
```

Lua returns several values natively; La3 expresses the same thing as one tuple, typed and destructured the same way.

Closures, from Rust.

```rust
// Rust
let double = |x| x * 2;
let scaled = move |x| x * base;
```

```la3
// La3
let double = |x| x * 2
let scaled = move |x| x * base
```

Async, from TypeScript.

```ts
// TypeScript
async function fetchJson<T>(url: string): Promise<T> {
  const resp = await fetch(url);
  return resp.json();
}
```

```la3
// La3
async fn fetch_json<T>(url: str) -> Result<T> {
    let resp = await http.get(url)
    resp.json::<T>()
}
```

`async`/`await` reads the same. La3 makes the fallible result explicit as `Result<T>` instead of relying on a thrown rejection.

## 6. Data Structures

### Structs

```la3
struct Point {
    x: f64,
    y: f64,
}

struct TlsRecord {
    content_type: u8,
    version: u16,
    length: u16,
    payload: List<u8>,
}
```

Construction uses `TypeName { field: value }` syntax. The struct-update spread `..other` copies the remaining fields (from Rust).

```la3
let origin = Point { x: 0.0, y: 0.0 }
let moved  = Point { x: 3.0,..origin }    // y: 0.0 copied from origin
```

Methods are defined in an `impl` block. `self` is the receiver, and associated functions with no receiver serve as constructors. Three receiver forms exist. `self` takes ownership, `mut self` takes ownership and allows the method to mutate its own local copy before returning it (the value-transform idiom), and `&self` / `&mut self` borrow without taking ownership (Section 11). `mut self` does not contradict immutability by default; the caller's binding is still consumed, and what mutates is the method's owned copy.

```la3
impl Point {
    fn new(x: f64, y: f64) -> Point {
        Point { x, y }    // shorthand: field name == variable name
    }

    fn distance(&self, other: &Point) -> f64 {
        let dx = self.x - other.x
        let dy = self.y - other.y
        (dx*dx + dy*dy) ** 0.5
    }

    fn translate(mut self, dx: f64, dy: f64) -> Point {
        self.x += dx
        self.y += dy
        self
    }
}

let p = Point.new(3.0, 4.0)
let d = p.distance(&Point.new(0.0, 0.0))    // 5.0
```

### Enums (from Rust)

Variants can carry data, which distinguishes Laila Lang enums from C-style integer enums. This is the key thing Laila Lang borrows from Rust.

```la3
enum Direction { North, South, East, West }

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(str),
}

enum Shape {
    Circle(f64),
    Rect { width: f64, height: f64 },
    Triangle(f64, f64, f64),    // three sides
}

enum Result<T> {
    Ok(T),
    Err(str),
}

enum Option<T> {
    Some(T),
    None,
}
```

`Result<T>` is an ordinary library enum spelled out here; nothing about it is special syntax. `Option<T>` has the *shape* of an ordinary enum, but the language adds one privileged rule on top of that shape. Its `None` variant is identified with the single absence value `nil`, so `None == nil`, and a bare `T | nil` and an `Option<T>` convert implicitly (Section 2). That rule is what lets the two absence spellings stay a single underlying value; it is the only place an enum receives special treatment, and it is deliberate rather than accidental.

### Lists, Slices, and Arrays

```la3
// Fixed-size array, length is part of the type, stored on the stack (from C/Rust)
let buf: [u8; 512]  = [0; 512]

// Slice, a borrowed view into an array or list, does not own data
fn process(data: &[u8]) {... }

// Growable list, heap-allocated, variable length
let mut log: List<str> = []
log.push("entry one")
log.push("entry two")
log.pop()            // removes and returns "entry two"
let len = log.len()  // 1
let slice = log[0..1]
```

### Maps

```la3
let mut headers: Map<str, str> = {}
headers["Content-Type"] = "application/json"
headers["Authorization"] = "Bearer " + token

let ct  = headers.get("Content-Type")    // Option<str>
let ct  = headers["Content-Type"]        // str; panics if absent

headers.remove("Authorization")

for (key, val) in headers {
    io.println(key + ": " + val)
}
```

### Sets

```la3
let mut seen: Set<str> = {}
seen.insert(ip)
let is_new = !seen.contains(ip)
```

### Design rationale

**Methods live in `impl`, separate from the struct.** Keeping the data layout (`struct`) apart from the behavior (`impl`) lets a reader see the shape of the data without scrolling past its methods, and it allows behavior to be added in more than one block (for instance one `impl` per interface). Construction uses `Type { field: value }` so every field is named at the call site, which keeps a constructor readable even with many fields and removes the positional-argument guessing that plagues long parameter lists.

**Enums carry data, and why that is the headline feature.** A C enum is just named integers. A Laila Lang enum is a sum type; each variant can carry its own data, so `Shape` is a `Circle(f64)` or a `Rect { width, height }` and nothing else. This lets the type make illegal states unrepresentable, a value that is "a circle with a radius" cannot also accidentally carry a width. Combined with exhaustive `match` (Section 7), the compiler then forces every site to handle every variant, so adding a variant later surfaces every place that must change. This pairing, sum types plus exhaustive matching, is the single most valuable idea borrowed from Rust, which is why `Option` and `Result` are themselves ordinary enums rather than built-in magic.

**Three sequence types because the hardware has three situations.** A fixed array `[T; N]` carries its length in its type and typically lives on the stack, which is what a protocol header or a hash output is. A slice `&[T]` is a borrowed window into someone else's storage, owning nothing, which is what a function should take when it only needs to read. A `List<T>` owns a resizable heap buffer, which is what you build up at runtime. These are different runtime things, so they get different notations; collapsing them would hide exactly the allocation and ownership facts that systems posts are about. The growable form is spelled `List<T>` rather than `[T]` specifically because `[T]` reads as a slice to the largest expert audience (Rust readers), and notation that misleads the expert is worse than notation that is merely unfamiliar.

**Braces for maps and sets.** `{ "a": 1 }` is a map and `{ "a", "b" }` is a set, reusing the brace literal most readers associate with "a collection written out." The form is disambiguated by its contents (key-value pairs versus bare values) and by the binding's type, so `{}` is an empty map or set according to its annotation. This keeps collection literals visually uniform with struct literals while staying unambiguous in practice.

### Side by side

Enums with data, from Rust.

```rust
// Rust
enum Shape {
    Circle(f64),
    Rect { width: f64, height: f64 },
}
```

```la3
// La3
enum Shape {
    Circle(f64),
    Rect { width: f64, height: f64 },
}
```

Identical; this is the central idea La3 takes from Rust. Contrast a C enum, whose variants are only integers and cannot carry data.

```c
// C: a variant is just a name for an integer
enum Shape { CIRCLE, RECT };   // the radius and width live elsewhere, by convention
```

Fixed-size arrays, from C.

```c
// C
uint8_t buf[512] = {0};
```

```la3
// La3
let buf: [u8; 512] = [0; 512]
```

Same idea, the length is part of the type; La3 writes the size inside the type as `[u8; 512]`.

Growable list, from Rust's `Vec`.

```rust
// Rust
let mut log: Vec<String> = Vec::new();
log.push("entry".into());
```

```la3
// La3
let mut log: List<str> = []
log.push("entry")
```

La3 calls it `List<T>` rather than `[T]` so it never reads as a Rust slice.

## 7. Control Flow

### `if` as Expression (from Rust)

```la3
// As a statement
if n < 0 {
    io.println("negative")
} else if n == 0 {
    io.println("zero")
} else {
    io.println("positive")
}

// As an expression, both branches must return the same type
let label = if score >= 90 { "A" }
             else if score >= 80 { "B" }
             else { "F" }
```

### `match` (from Rust)

`match` is exhaustive; the compiler requires every possible value to be handled, either explicitly or via a `_` wildcard. It is also an expression.

```la3
match code {
    200       => io.println("OK"),
    301 | 302 => io.println("redirect"),
    404       => io.println("not found"),
    500..=599 => io.println("server error"),
    _         => io.println("unexpected: " + str(code)),
}

let description = match code {
    200       => "OK",
    404       => "Not Found",
    _         => "Other",
}
```

Pattern variants.

```la3
// Match a range
match byte {
    0x00        => "null",
    0x01..=0x1F => "control",
    0x20..=0x7E => "printable ASCII",
    0x7F        => "DEL",
    _           => "extended",
}

// Destructure an enum variant
match addr {
    IpAddr.V4(a, b, c, d) => f"{a}.{b}.{c}.{d}",
    IpAddr.V6(s)          => s,
}

// Guard with `if`, the pattern only matches when the guard is true
match shape {
    Shape.Circle(r) if r > 1000.0 => io.println("enormous circle"),
    Shape.Circle(r)               => io.println(f"circle r={r}"),
    _                             => io.println("other"),
}

// Bind the matched value with `@`
match n {
    x @ 1..=12 => io.println(f"month {x}"),
    _          => io.println("invalid month"),
}
```

`if let` does single-variant matching without a full `match`.

```la3
if let Some(port) = config.port {
    bind(port)
}

if let IpAddr.V4(a, b, c, d) = addr {
    io.println(f"IPv4: {a}.{b}.{c}.{d}")
}
```

`while let` loops as long as a pattern matches.

```la3
while let Some(item) = queue.pop() {
    process(item)
}
```

### Loops

```la3
// loop, infinite, break with a value (from Rust)
let result = loop {
    let line = io.read_line()
    match parse(line) {
        Ok(v)  => break v,         // loop evaluates to v
        Err(_) => continue,        // try again
    }
}

// while
while !stream.is_closed() {
    let chunk = stream.read_chunk()
    buffer.append(chunk)
}

// for..in over any iterable
for packet in capture {
    inspect(packet)
}

// Numeric range (end exclusive)
for i in 0..n {
    arr[i] = compute(i)
}

// Inclusive range
for i in 1..=10 {
    io.println(str(i))
}

// Enumerate, yields (index, value) pairs
for (i, item) in items.enumerate() {
    io.println(f"{i}: {item}")
}
```

### Design rationale

**`if` and `match` are expressions.** Because they produce values, control flow composes; a value can be computed by branching with no mutable temporary, and the type checker verifies that every branch yields the same type. This is the same reasoning as "blocks are expressions" in Section 5, applied to branching.

**Exhaustive `match`.** The compiler rejects a `match` that does not cover every possible value. For beginners this catches the "I forgot the `nil` case" bug before it can run. For advanced readers it is a maintenance tool, adding a variant to an enum turns every now-incomplete `match` into a compile error that points straight at the code that must change, which is how large programs evolve safely. The `_` wildcard is there when a catch-all is genuinely intended, so exhaustiveness is a help rather than a straitjacket.

**`if let` and `while let`.** A full `match` to handle one variant and ignore the rest is more ceremony than the situation deserves, so `if let Some(x) = opt` and `while let Some(x) = queue.pop()` are the ergonomic short forms. They read as "if this matches, bind and proceed," which is exactly the intent.

**`loop` with a break value.** A dedicated infinite loop states "this repeats until something inside stops it" more honestly than `while true`, and allowing `break value` lets the loop be an expression that computes a result (retry until a parse succeeds, then yield the parsed value). The alternative, a mutable variable set inside the loop and read afterward, is precisely the mutable-state pattern the rest of the language avoids.

### Side by side

`match` as an expression, from Rust.

```rust
// Rust
let description = match code {
    200 => "OK",
    404 => "Not Found",
    _   => "Other",
};
```

```la3
// La3
let description = match code {
    200 => "OK",
    404 => "Not Found",
    _   => "Other",
}
```

The same exhaustiveness and the same expression form; only the trailing semicolon differs.

## 8. Error Handling

Laila Lang has two error mechanisms with a clear division of labor. `Result<T>` models recoverable failure inside Laila Lang code, where the caller is expected to handle the outcome. `try`/`catch` models the boundary with external systems that signal failure by throwing. The rule is simple; if you write the failing function, return `Result`, and if you call into something that throws (I/O, network, FFI), wrap it in `try`/`catch`.

### `Result<T>` (from Rust)

`Result<T>` is either `Ok(value)` or `Err(message)`. Functions that can fail return a `Result`.

```la3
fn read_config(path: str) -> Result<Config> {
    let text = fs.read(path)?           // ? propagates Err immediately
    let cfg  = json.decode::<Config>(text)?
    Ok(cfg)
}
```

The `?` operator (from Rust); if the value is `Err(e)`, the enclosing function returns `Err(e)` immediately, and if it is `Ok(v)`, the expression evaluates to `v`. It can appear only inside functions that return `Result` or `Option`.

Handling a `Result`.

```la3
match read_config("config.json") {
    Ok(cfg)  => apply(cfg),
    Err(e)   => {
        io.println("failed to load config: " + e)
        os.exit(1)
    }
}
```

Convenience methods avoid a verbose `match` for common patterns.

```la3
let cfg = read_config("config.json").unwrap()               // panics on Err
let cfg = read_config("config.json").unwrap_or(default_config())
let cfg = read_config("config.json").unwrap_or_else(|_| make_default())
let n   = parse_num(s).map(|v| v * 2)                       // Ok(v*2) or Err unchanged
let v   = load(path).and_then(|raw| parse(raw))             // chain Result-returning fns
let msg = op().map_err(|e| "operation failed: " + e)        // transform the error
```

### `Option<T>`

```la3
let first: Option<str> = items.first()

if let Some(v) = first {
    process(v)
}

let v = first.unwrap_or("default")
let v = first.map(|s| s.to_upper())
let v = first?    // in an Option-returning function: return None if first is None
```

### `try` / `catch`

When calling into external systems that use exception-style signaling, Laila Lang uses `try`/`catch` blocks. This is intentionally distinct from `Result`; it marks the boundary between Laila Lang's value-based error model and external exception-based systems. Unlike `Err(str)`, external exception objects carry a `.message` field and can be caught by named type.

```la3
try {
    let conn = db.connect(dsn)
    let rows = conn.query("SELECT id, name FROM users WHERE active = 1")
    for row in rows {
        process(row)
    }
} catch e: ConnectionError {
    io.println("database unreachable: " + e.message)
    os.exit(1)
} catch e {
    io.println("unexpected error: " + str(e))
} finally {
    db.close()
}
```

### Design rationale

**Two mechanisms, one boundary.** Having both `Result` and `try`/`catch` looks redundant until you notice they describe two genuinely different situations. `Result<T>` is for failure the author of a function anticipates and the caller is expected to handle; the failure is part of the function's contract, so it is part of its return type. `try`/`catch` is for the edge of the program where it meets systems that signal failure by throwing (operating-system calls, network libraries, foreign code), where there is no `Result` to inspect because the failure arrives as an exception. The rule that resolves every real case is mechanical. If you wrote the failing function, return `Result`; if you are calling something that throws, wrap it in `try`/`catch`. The two never compete for the same call, so the apparent redundancy is actually a clean division of labor between Laila Lang's own value-based errors and the exception-based world outside it.

**Why errors are values, and why `?`.** Representing a recoverable error as a returned `Result` rather than an unwound exception keeps the failure visible in the type, where it cannot be silently ignored; you must `match` it, `unwrap` it, or propagate it with `?`. `?` exists so that propagation, the common case, costs one character instead of a five-line `match`, keeping the happy path readable. `Err(str)` carries a human message rather than a typed error hierarchy because, in illustrative blog code, a message is what the reader needs to understand the failure; a production language would likely use a typed error, and a post on that topic is free to show one.

### Side by side

`try`/`catch`, from TypeScript. (The `Result`/`?` comparison appears in Section 5.)

```ts
// TypeScript
try {
  const conn = await db.connect(dsn);
  for (const row of await conn.query(sql)) process(row);
} catch (e) {
  console.error("db error:", e.message);
} finally {
  db.close();
}
```

```la3
// La3
try {
    let conn = db.connect(dsn)
    for row in conn.query(sql) { process(row) }
} catch e: ConnectionError {
    io.eprintln("db error: " + e.message)
} finally {
    db.close()
}
```

The shape is TypeScript's. La3 adds typed `catch` clauses and reserves the whole construct for the external, exception-throwing boundary, using `Result` everywhere inside its own code.

## 9. Interfaces and Trait Bounds

A Laila Lang interface describes a set of methods a type must implement. The declaration syntax follows TypeScript's structural style, but satisfaction is nominal; a type satisfies an interface only when there is an explicit `impl` block for it, never merely by having methods with the right shapes. This combination (familiar declaration, explicit opt-in) is deliberate, it keeps the declaration readable while making conformance unambiguous at a glance.

```la3
interface Encode {
    fn encode(self) -> List<u8>
}

interface Decode {
    fn decode(raw: &[u8]) -> Result<Self>
}

// A combined bound
interface Codec: Encode + Decode {}
```

Implementation.

```la3
struct Frame {
    id: u16,
    payload: List<u8>,
}

impl Encode for Frame {
    fn encode(self) -> List<u8> {
        let mut out: List<u8> = []
        out.push((self.id >> 8) as u8)
        out.push((self.id & 0xFF) as u8)
        out.extend(&self.payload)
        out
    }
}
```

A generic function constrained to an interface.

```la3
fn send<T: Encode>(conn: &mut TcpStream, value: T) -> Result<()> {
    let bytes = value.encode()
    conn.write_all(&bytes)
}
```

### Design rationale

**Structural declaration, nominal conformance, and why the hybrid.** The two mainstream models each get one thing right and one thing wrong for a reading language. Go's structural interfaces (a type conforms automatically if it has the right methods) are wonderfully terse to declare, but they make conformance invisible; a reader cannot tell from a type whether it was meant to satisfy an interface or merely happens to. Rust's traits are explicit (`impl Trait for Type`), so conformance is unmistakable, but the trait declaration itself is heavier. Laila Lang takes the readable half of each. The declaration looks like a TypeScript interface, a plain list of method shapes, and conformance is an explicit `impl Encode for Frame` block as in Rust. The result is that both questions a reader asks, "what does this interface require" and "does this type promise to satisfy it," have answers visible on the page rather than inferred. The cost is one `impl` block per conformance, which is also where the methods are written, so it adds no real ceremony.

### Side by side

The hybrid in one view, the declaration is TypeScript-shaped and the conformance is Rust-shaped.

```ts
// TypeScript: structural; a type conforms automatically if its methods match
interface Encode { encode(): Uint8Array }
```

```rust
// Rust: nominal; conformance is an explicit impl
trait Encode { fn encode(&self) -> Vec<u8>; }
impl Encode for Frame { fn encode(&self) -> Vec<u8> { /*... */ } }
```

```la3
// La3: TypeScript-style declaration, Rust-style explicit impl
interface Encode { fn encode(self) -> List<u8> }
impl Encode for Frame { fn encode(self) -> List<u8> {... } }
```

La3 reads like the TypeScript declaration but requires the explicit `impl` like Rust, so conformance is never accidental.

## 10. Modules

```la3
use net::http
use net::dns
use crypto::{sha256, hmac_sha256}
use std::io::{println, read_file, write_file}
use std::collections::{Map, Set}
```

Defining a module.

```la3
mod codec {
    pub fn encode(frame: &Frame) -> List<u8> {... }   // pub = visible outside mod
    pub fn decode(raw: &[u8]) -> Result<Frame> {... }

    fn checksum(data: &[u8]) -> u32 {... }            // private to mod
}

use codec::{encode, decode}
```

Items are private to their module unless marked `pub`. Paths use `::` to separate module segments and `.` to access fields, methods, and enum variants.

### Design rationale

**Private by default, `pub` to expose.** A module's surface area should be a deliberate choice, not the accidental sum of everything defined inside it. Defaulting to private lets a reader trust that a non-`pub` function is an internal detail with no callers outside the module, which makes the module easier to reason about and to change. `pub` then reads as "this is part of the contract."

**Two separators, `::` and `.`, on purpose.** Paths through the module and type namespace use `::` (`net::http`, `Type::method`), while access into a value uses `.` (`response.status`, `frame.encode()`). Keeping them distinct lets a reader tell at a glance whether `a::b` is navigating the static structure of the program or `a.b` is reaching into a runtime value, a distinction a single dot would blur. This follows Rust, and it is one of the few places where a little extra punctuation buys real clarity.

### Side by side

Imports, from Rust.

```rust
// Rust
use crypto::{sha256, hmac_sha256};
use std::collections::{HashMap, HashSet};
```

```la3
// La3
use crypto::{sha256, hmac_sha256}
use std::collections::{Map, Set}
```

The `use` path syntax and the `::` separators are Rust's; La3 renames the collections to `Map`/`Set`.

## 11. Memory Model

Most Laila Lang code does not need to think about memory. This section exists for posts about systems programming, operating system internals, and language implementation.

### References (safe, from Rust)

A reference is a pointer with lifetime tracking. `&T` is a shared (read-only) reference, and `&mut T` is an exclusive (writable) reference. At any point a value has either any number of `&T` references or exactly one `&mut T` reference, never both.

```la3
fn sum(xs: &[i32]) -> i32 {    // borrows xs, does not copy or own it
    xs.reduce(0, |a, x| a + x)
}

fn double_in_place(x: &mut i32) {
    *x *= 2    // * dereferences the reference
}

let mut n = 21
double_in_place(&mut n)    // n is now 42
```

### Raw Pointers (from C)

Raw pointers are `*T` (read-only) and `*mut T` (writable). Unlike references, they carry no lifetime or aliasing guarantees. Dereferencing a raw pointer requires an `unsafe` block, a signal to the reader that the invariants are being maintained by human reasoning, not by the compiler.

```la3
let arr: [i32; 5] = [10, 20, 30, 40, 50]
let p: *i32 = &raw arr[0]   // raw pointer to the first element

unsafe {
    let third = *(p + 2)    // pointer arithmetic: p + 2 advances by 2 × sizeof(i32) = 8 bytes
    // third == 30
}
```

`&raw` takes a raw pointer (distinct from `&`, which takes a safe reference). The compiler knows the pointed-to type and scales arithmetic accordingly; `p + n` advances the address by `n * sizeof(T)`, not by `n` bytes. This is why going out of bounds is not automatically caught; the arithmetic produces a valid-looking address regardless of whether the target memory belongs to the current allocation.

Heap allocation.

```la3
// Allocate n bytes; returns a raw pointer to the first byte
let p: *mut u8 = alloc(n)

// Must be freed with the same size
dealloc(p, n)
```

`alloc` and `dealloc` correspond to `malloc`/`free` in C or the raw allocator API in Rust. They appear in posts about heap internals; ordinary Laila Lang code uses `List` and `Map`, which manage memory automatically.

### Pointer Arithmetic

```la3
let base: *i32 = &raw buf[0]
let elem: *i32 = base + 3    // address of buf[3]

// arr[i] and *(arr_ptr + i) are the same operation
let v1 = arr[3]
let v2 = unsafe { *(base + 3) }
// v1 == v2
```

### Design rationale

**Two pointer-like things because there are two different promises.** A reference (`&T`, `&mut T`) is the safe one; the compiler tracks how long it lives and enforces that a value has either many readers or one writer at a time, never both. That single rule, aliasing xor mutability, is what lets a reader (and the compiler) be sure a value is not being changed out from under another part of the code, and it is also what lets an optimizer reorder memory accesses safely. A raw pointer (`*T`, `*mut T`) makes none of those promises; it is just an address, exactly as in C, and it exists because posts about how memory physically works need to talk about addresses with the safety machinery out of the way.

**Why `&raw` is separate from `&`.** Taking a safe reference and taking a raw pointer are different acts with different guarantees, so they get different syntax; `&x` produces a tracked reference while `&raw x` produces a bare address. An earlier draft used `&` for both, which quietly implied that a raw pointer carried a reference's guarantees. Separating them makes the loss of safety visible at the exact point it happens.

**`unsafe` as a sign for the reader.** Dereferencing a raw pointer is wrapped in `unsafe {... }` not because the block does anything special at runtime, but because it marks the region where the compiler has stopped checking and a human is vouching for correctness. In a teaching text that marker is the whole point; it tells the reader "the invariants here are maintained by argument, not by the type system, so read carefully."

**Pointer arithmetic is scaled by type.** `p + n` advances by `n * sizeof(T)`, not `n` bytes, matching C, so `p + 1` points at the next element rather than the next byte. This is why an out-of-bounds offset is not caught automatically; the arithmetic produces a plausible address whether or not it still belongs to the original allocation, which is precisely the hazard that posts about memory safety need to be able to show.

### Side by side

Safe references, from Rust.

```rust
// Rust
fn double_in_place(x: &mut i32) { *x *= 2; }
let mut n = 21;
double_in_place(&mut n);   // n == 42
```

```la3
// La3
fn double_in_place(x: &mut i32) { *x *= 2 }
let mut n = 21
double_in_place(&mut n)    // n == 42
```

Raw pointers, from C.

```c
// C
int arr[5] = {10, 20, 30, 40, 50};
int *p = &arr[0];
int third = *(p + 2);   // 30
```

```la3
// La3
let arr: [i32; 5] = [10, 20, 30, 40, 50]
let p: *i32 = &raw arr[0]
let third = unsafe { *(p + 2) }   // 30
```

The pointer arithmetic matches C exactly. La3 adds `&raw` to mark the unsafe address-taking and wraps the dereference in `unsafe`, neither of which C requires, because the whole point of those markers is to make the loss of safety visible to a reader.

## 12. Concurrency

### Threads

```la3
let handle = spawn {
    // runs concurrently in a new OS thread
    let result = compute_heavy()
    result
}

let value = handle.join()    // blocks until thread completes; returns its value
```

Multiple threads communicating via channels.

```la3
let ch = channel<str>(capacity: 32)    // buffered channel

spawn {
    for packet in capture {
        ch.send(inspect(packet))
    }
    ch.close()
}

for report in ch {    // iterates until channel is closed
    io.println(report)
}
```

### Async Concurrency

`async`/`await` is single-threaded concurrency; tasks are interleaved at `await` points, not run on parallel threads. Spawning the futures starts them; awaiting is where control can switch between them.

```la3
async fn resolve_all(hosts: List<str>) -> Map<str, Result<List<str>>> {
    let jobs = hosts.map(|h| async { (h, await dns.resolve(h)) })
    let pairs = await all(jobs)
    pairs.collect()
}
```

`race` resolves with the first future to complete.

```la3
async fn fetch_with_timeout(url: str, ms: u64) -> Result<Response> {
    let fetching = http.get(url)
    let timeout  = async {
        await os.sleep(ms)
        Err("timeout after " + str(ms) + "ms")
    }
    await race(fetching, timeout)
}
```

### Design rationale

**Threads and async are different tools, so both exist.** `spawn` plus `join` is real parallelism, separate OS threads that can run at the same instant on different cores, which is what you want for CPU-bound work such as hashing or compression. `async`/`await` is concurrency without parallelism, a single thread interleaving many tasks at their `await` points, which is what you want for I/O-bound work where the program spends its time waiting on the network or disk rather than computing. Conflating them would hide the property that actually matters for a given post, whether these things run at the same time or merely take turns, so the language keeps both notations and uses them to mean exactly those two different things.

**Channels for thread communication.** Threads share work by sending values over a channel rather than by sharing mutable memory, which keeps the immutability-and-ownership story intact across thread boundaries; the value is handed off, not aliased. Iterating a channel until it closes (`for x in ch`) reads as a stream of results, which is usually what concurrent producers are.

**`all` and `race` name the two common await patterns.** Waiting for every future (`all`) and taking whichever finishes first (`race`) are the two things concurrent code almost always wants, so they are named primitives rather than something each post re-derives. `race` is also how a timeout is expressed, run the real work against a sleep and take whichever wins.

### Side by side

Threads, from Rust.

```rust
// Rust
let handle = std::thread::spawn(|| compute_heavy());
let value = handle.join().unwrap();
```

```la3
// La3
let handle = spawn { compute_heavy() }
let value = handle.join()
```

Awaiting many futures, from TypeScript.

```ts
// TypeScript
const results = await Promise.all(hosts.map(h => ping(h)));
```

```la3
// La3
let results = await all(hosts.map(|h| ping(h)))
```

`Promise.all` becomes `all`, and the rest reads the same.

## 13. Standard Library

The standard library is a set of many small, fully independent modules rather than one monolithic runtime, and a program is understood to draw in only the modules it actually uses. The organizing rules behind that are spelled out at the end of this section. The following namespaces are available in any Laila Lang example without an explicit `use`. Signatures use `->` for synchronous and `-> async` for asynchronous return types.

### `io`

```la3
io.print(v: any)
io.println(v: any)
io.read_line() -> str
io.read_all() -> str
io.eprintln(v: any)    // stderr
```

### `fs`

```la3
fs.read(path: str) -> Result<str>
fs.read_bytes(path: str) -> Result<List<u8>>
fs.write(path: str, content: str) -> Result<()>
fs.write_bytes(path: str, data: &[u8]) -> Result<()>
fs.open(path: str, mode: str) -> Result<File>

file.write(&[u8]) -> Result<()>
file.read_to_str() -> Result<str>
file.seek(offset: i64, from: SeekFrom) -> Result<u64>
file.close()
```

### `net`

```la3
http.get(url: str) -> async Result<Response>
http.post(url: str, body: str, headers: Map<str, str>) -> async Result<Response>

response.status: u16
response.headers: Map<str, str>
response.body: str
response.json::<T>() -> Result<T>
response.bytes() -> List<u8>

dns.resolve(host: str) -> async Result<List<str>>
dns.reverse(ip: str) -> async Result<str>

tcp.listen(addr: str, port: u16) -> Result<TcpListener>
tcp.connect(addr: str, port: u16) -> async Result<TcpStream>
stream.read(&mut [u8]) -> async Result<usize>
stream.write(&[u8]) -> async Result<()>
stream.close()
```

### `str` (methods on string values)

```la3
s.len() -> usize
s.is_empty() -> bool
s.contains(sub: str) -> bool
s.starts_with(prefix: str) -> bool
s.ends_with(suffix: str) -> bool
s.to_upper() -> str
s.to_lower() -> str
s.trim() -> str
s.trim_start() -> str
s.trim_end() -> str
s.split(sep: str) -> List<str>
s.split_once(sep: str) -> Option<(str, str)>
s.replace(from: str, to: str) -> str
s.repeat(n: usize) -> str
s.parse::<T>() -> Result<T>
s.as_bytes() -> &[u8]
s.chars() -> List<char>
```

String formatting uses the `f"..."` prefix.

```la3
let msg = f"host={host} port={port} tls={tls}"
let hex = f"0x{byte:02x}"           // lowercase hex, zero-padded to width 2
let pct = f"{ratio * 100:.1f}%"     // one decimal place
let pad = f"{name:>20}"             // right-aligned in a field of width 20
```

### `List<T>` (methods on lists)

```la3
xs.len() -> usize
xs.is_empty() -> bool
xs.push(v: T)
xs.pop() -> Option<T>
xs.first() -> Option<T>
xs.last() -> Option<T>
xs.contains(v: &T) -> bool
xs.extend(other: &[T])
xs.map(f: fn(T) -> U) -> List<U>
xs.filter(pred: fn(&T) -> bool) -> List<T>
xs.reduce(init: U, f: fn(U, T) -> U) -> U
xs.sort_by(cmp: fn(&T, &T) -> bool) -> List<T>
xs.group_by(key: fn(&T) -> K) -> Map<K, List<T>>
xs.enumerate() -> List<(usize, T)>
xs.zip(other: List<U>) -> List<(T, U)>
```

### `bytes`

```la3
bytes.from_hex(s: str) -> Result<List<u8>>
bytes.to_hex(b: &[u8]) -> str
bytes.from_base64(s: str) -> Result<List<u8>>
bytes.to_base64(b: &[u8]) -> str
bytes.compare(a: &[u8], b: &[u8]) -> bool    // constant-time
```

### `crypto`

```la3
crypto.sha256(data: &[u8]) -> [u8; 32]
crypto.sha512(data: &[u8]) -> [u8; 64]
crypto.sha3_256(data: &[u8]) -> [u8; 32]
crypto.hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32]
crypto.random_bytes(n: usize) -> List<u8>
```

### `json`

```la3
json.encode(value: any) -> Result<str>
json.decode::<T>(s: str) -> Result<T>
json.pretty(value: any) -> Result<str>
```

### `os`

```la3
os.args() -> List<str>
os.env(key: str) -> Option<str>
os.exit(code: i32) -> !       // ! means this function never returns
os.sleep(ms: u64) -> async ()
os.now() -> u64               // Unix timestamp in milliseconds
```

### `math`

```la3
math.floor(x: f64) -> f64
math.ceil(x: f64) -> f64
math.round(x: f64) -> f64
math.abs(x: f64) -> f64
math.sqrt(x: f64) -> f64
math.log(x: f64) -> f64           // natural log
math.log2(x: f64) -> f64
math.sin(x: f64) -> f64
math.cos(x: f64) -> f64
math.pi: f64                       // 3.14159265358979...
math.e: f64                        // 2.71828182845904...
math.inf: f64
```

### Async primitives

```la3
// Resolves when all futures complete; returns a list of their results in order
all(futures: List<async T>) -> async List<T>

// Resolves with the result of whichever future finishes first
race(futures:...async T) -> async T
```

`all` and `race` are top-level async primitives, available without a `use` statement.

### Module independence and the two profiles

The standard library is deliberately not a single block of code that a program either takes whole or splits with a `no_std` switch. It is a set of many small modules, and a program draws in only the ones it actually uses. Three properties hold this together, and they are stated here so that example code can rely on them.

**Independence is the invariant.** Each module is self-contained. Used on its own, a module depends on nothing else in the library; there are no hard edges from one module to another. A post can show `crypto` in isolation and the reader can trust that nothing about `crypto` quietly drags in `net` or `json`.

**Only what is used is present.** A program pulls in the modules it touches and no others. Reaching for `bytes.to_hex` brings in `bytes`; it does not bring in `math` or `fs`. This is dead-code elimination at the granularity of a module, and it is what lets the same library serve both a one-line script and a full application without the script paying for the application's surface.

**Sharing is opportunistic, never structural.** If a program happens to use two modules together, and one of them could be expressed more cheaply by reusing something the other already provides, the first may fold onto the second's implementation and become smaller. The restriction that makes this safe is that it only ever removes duplication already present; it never creates a dependency in the alone case. A module that shares with a neighbour when the neighbour is there is still, used by itself, an island.

From these properties follow two profiles for the same library.

The **granular** profile is independence plus opportunistic sharing plus aggressive elimination of everything unused, the smallest possible surface. It is the profile to imagine when a post is about an embedded target, a bootloader, or a kernel, where every module that appears is one the code genuinely needs.

The **monolithic** profile is the default. Here the whole library is free to interdepend, tuned for ordinary programs where total size is not the constraint and convenience is.

The two profiles are required to agree on behaviour. The same program, read under the granular profile or the monolithic one, does exactly the same thing; only its size and internal layout differ. This is the load-bearing guarantee, because it lets a post discuss the granular profile for a systems audience without ever implying that the code would behave differently in an ordinary setting.

### Design rationale

**Why à-la-carte and not `no_std`.** The familiar way to make a standard library fit a small target is to split it into a core that needs no operating system and an upper half that does, then ask embedded code to opt out of the upper half. That split is coarse; it draws one line through the whole library and forces every facility onto one side of it. The à-la-carte model draws no such line. Each facility is its own module, and the program's actual usage rather than a global switch decides what is present. The reader of an embedded post then sees a library that contains exactly the modules in play, which is both smaller and more honest about what the code depends on.

**Why independence is an invariant rather than a goal.** If modules were merely "loosely coupled," a reader could never be sure that a single module, shown alone, did not silently require another. Making independence a hard rule means a module shown in isolation is genuinely complete, which is precisely what a focused example needs. Opportunistic sharing is then admitted only because it can never violate that rule; it removes duplication when two modules already coexist and adds coupling in no case, so the property a reader relies on survives the optimization.

**Why two profiles instead of one.** A single library tuned for full applications would carry weight that an embedded post should not have to explain, and a single library tuned for the smallest target would make ordinary examples awkward. Two profiles let each kind of post show the library in the form that suits it, and the behavioural-identity guarantee keeps that from becoming two different languages. The granular profile is a lens for size-sensitive writing; the monolithic profile is the everyday default.

## 14. A Complete Example

To make the above concrete, here is a Laila Lang program that encodes and decodes a minimal TLS record, the kind of code that would appear in a post about network protocols.

```la3
use crypto::sha256
use bytes::{from_hex, to_hex}

const TLS_RECORD_HEADER_LEN: usize = 5

struct TlsRecord {
    content_type: u8,    // 20=ChangeCipherSpec 21=Alert 22=Handshake 23=AppData
    version_major: u8,
    version_minor: u8,
    payload: List<u8>,
}

impl TlsRecord {
    fn encode(self) -> List<u8> {
        let length = self.payload.len() as u16
        let mut out: List<u8> = []
        out.push(self.content_type)
        out.push(self.version_major)
        out.push(self.version_minor)
        out.push((length >> 8) as u8)    // big-endian length
        out.push((length & 0xFF) as u8)
        out.extend(&self.payload)
        out
    }

    fn decode(raw: &[u8]) -> Result<TlsRecord> {
        if raw.len() < TLS_RECORD_HEADER_LEN {
            return Err(f"too short: got {raw.len()} bytes, need at least {TLS_RECORD_HEADER_LEN}")
        }
        let length = ((raw[3] as u16) << 8) | (raw[4] as u16)
        if raw.len() < TLS_RECORD_HEADER_LEN + length as usize {
            return Err("truncated payload")
        }
        Ok(TlsRecord {
            content_type:  raw[0],
            version_major: raw[1],
            version_minor: raw[2],
            payload:       raw[TLS_RECORD_HEADER_LEN..TLS_RECORD_HEADER_LEN + length as usize],
        })
    }
}

fn main() -> Result<()> {
    let record = TlsRecord {
        content_type:  22,    // Handshake
        version_major: 3,
        version_minor: 3,     // TLS 1.2
        payload:       [0x01, 0x00, 0x00, 0x04, 0xDE, 0xAD, 0xBE, 0xEF],
    }

    let encoded = record.encode()
    io.println(f"encoded: {to_hex(&encoded)}")

    let decoded = TlsRecord.decode(&encoded)?
    io.println(f"content_type: {decoded.content_type}")
    io.println(f"payload: {to_hex(&decoded.payload)}")

    Ok(())
}
```

## 15. Reference Grammar

The following EBNF sketches the core grammar. It is descriptive rather than exhaustive; it captures the shapes that appear in blog examples. Terminals are in quotes, `*` is zero or more, `+` is one or more, `?` is optional, and `|` separates alternatives.

```ebnf
program     = item* ;
item        = use_decl | mod_decl | fn_decl | struct_decl
            | enum_decl | interface_decl | impl_decl | type_alias | const_decl ;

use_decl    = "use" path ("::" "{" ident ("," ident)* "}")? ;
mod_decl    = "pub"? "mod" ident "{" item* "}" ;

fn_decl     = "pub"? "async"? "fn" ident generics? "(" params? ")"
              ("->" ret_type)? block ;
params      = param ("," param)* ("," "..." ident ":" type)? ;
param       = ("mut"? "self") | (ident ":" type) ;
generics    = "<" generic ("," generic)* ">" ;
generic     = ident (":" bound ("+" bound)*)? ;

struct_decl = "pub"? "struct" ident generics? "{" field ("," field)* ","? "}" ;
field       = ident ":" type ;
enum_decl   = "pub"? "enum" ident generics? "{" variant ("," variant)* ","? "}" ;
variant     = ident ( "(" type ("," type)* ")" | "{" field ("," field)* "}" )? ;

interface_decl = "interface" ident (":" bound ("+" bound)*)?
                 "{" fn_sig* "}" ;
impl_decl   = "impl" (ident "for")? type "{" fn_decl* "}" ;
type_alias  = "type" ident "=" type ;
const_decl  = "const" ident ":" type "=" expr ;

type        = path generic_args?
            | "&" "mut"? type            (* reference *)
            | "*" "mut"? type            (* raw pointer *)
            | "[" type ";" expr "]"      (* fixed array *)
            | "&" "[" type "]"           (* slice *)
            | "(" (type ("," type)*)? ")" (* tuple/unit *)
            | type ("|" type)+           (* union *)
            | "async"? type ;
generic_args = "<" type ("," type)* ">" ;

stmt        = let_stmt | expr_stmt | item ;
let_stmt    = "let" "mut"? pattern (":" type)? "=" expr ;
expr_stmt   = expr ";"? ;

expr        = literal | path | block | if_expr | match_expr
            | loop_expr | while_expr | for_expr | closure | call
            | unary expr | expr binop expr | expr "?" | "await" expr
            | "spawn" block | "unsafe" block | try_expr ;

block       = "{" stmt* expr? "}" ;
if_expr     = "if" expr block ("else" (if_expr | block))? ;
match_expr  = "match" expr "{" (pattern guard? "=>" expr ",")* "}" ;
guard       = "if" expr ;
loop_expr   = "loop" block ;
while_expr  = "while" (expr | "let" pattern "=" expr) block ;
for_expr    = "for" pattern "in" expr block ;
closure     = "move"? "|" (ident (":" type)?)* "|" ("->" type)? (expr | block) ;
try_expr    = "try" block ("catch" (ident (":" type)?)? block)* ("finally" block)? ;

pattern     = "_" | literal | ident | "(" pattern ("," pattern)* ")"
            | path "(" pattern ("," pattern)* ")"
            | path "{" (ident ("," ident)*)? "}"
            | "[" pattern ("," pattern)* ("," ".." ident)? "]"
            | ident "@" pattern | range_pattern | ident ":" type ;

binop       = "+" | "-" | "*" | "/" | "%" | "**" | "//"
            | "==" | "!=" | "<" | ">" | "<=" | ">="
            | "&&" | "||" | "??" | "&" | "|" | "^" | "<<" | ">>"
            | ".." | "..=" | "as" ;
unary       = "!" | "-" | "~" | "*" | "&" | "&raw" ;
```

## 16. Summary of Sources

| Feature | Taken from | Notes |
| ------- | ---------- | ----- |
| `let` / `let mut` / `const` | Rust | Immutability by default |
| Integer types `i8`…`u64`, `usize` | Rust and C | Full numeric range for low-level posts |
| `fn`, `->`, implicit return | Rust | Blocks are expressions |
| Closures `\|x\|` and `move` | Rust | Captures, by-reference and `move` |
| `match` with exhaustiveness | Rust | Pattern binding, guards, ranges |
| `if let`, `while let` | Rust | Single-case matching idioms |
| `Result<T>`, `?` operator | Rust | Value-based error propagation |
| `loop { break value }` | Rust | Loop as expression |
| Enums with data | Rust | Algebraic types |
| `impl`, interface | Rust | Method dispatch, nominal conformance |
| `&T`, `&mut T`, `unsafe` | Rust | Safe reference model |
| Growable `List<T>` | Rust (`Vec<T>`) | Renamed so `[T]` never reads as a slice |
| `*T`, `*mut T`, `&raw`, pointer arithmetic | C | Low-level memory sections |
| `alloc`/`dealloc` | C | Heap illustration |
| Fixed-size arrays `[T; N]` | C and Rust | Stack-allocated buffers |
| `~` bitwise NOT | C | Distinct from `!` logical NOT |
| `async fn`, `await` | TypeScript/JS | Async concurrency |
| `Promise.all` → `await all(...)` | TypeScript/JS | Concurrent futures |
| `try`/`catch`/`finally` | TypeScript/JS | External exception boundary |
| Union types `T \| U` | TypeScript | Type narrowing in `match` |
| `??` null-coalescing, `?.` optional chaining | TypeScript/JS | Defined on `T \| nil` |
| Interface declaration style | TypeScript | Structural shape, explicit `impl` to conform |
| `nil` | Lua | Single null concept |
| Multiple return values | Lua | `(a, b) = f()` |
| `//` floor division | Lua | Integer floor, distinct from `/` |
| `f"..."` string interpolation | Python (format) | Readable in documentation |
