
## Comments

Comments start with `#` and run to the end of the line. There are no block comments.

```vary
# This is a comment
let x = 42  # inline comment
```

## Variables

Use `let` for immutable bindings and `mut` for mutable ones. Type annotations are optional when the type can be inferred.

```vary
let x = 42
let name: Str = "Alice"
mut counter = 0
counter = counter + 1
counter += 1            # compound assignment (also -=, *=, /=)
```

Variables declared at module level are accessible from all functions in the module:

```vary
let MAX_SIZE = 100

def check_size(n: Int) -> Bool {
    return n <= MAX_SIZE
}
```

## Types

Every value has a type known at compile time. See [Types](/docs/types/) for the full reference.

### Primitives

| **Type** | **Example** |
|------|---------|
| `Int` | `42` |
| `Float` | `3.14` |
| `Bool` | `True`, `False` |
| `Str` | `"hello"` |
| `Never` | return type for functions that never return (e.g. `exit()`) |

### Optional types

Append `?` to any type to make it optional. Optional values are either a value of that type or `None`.

```vary-snippet
let result: Int? = find("Alice")

if result is not None {
    print(result + 1)       # narrowed to Int inside this block
}
if result is None {
    print("not found")
}

let value = result ?: 0     # elvis: use 0 if None
let upper = name?.upper()   # safe call: returns None if name is None
let forced = result!!       # non-null assertion (throws if None)
```

`is None` and `is not None` are the idiomatic way to check for `None`. The equality forms `== None` and `!= None` also work and trigger the same flow narrowing.

### Tuples

Fixed-size groups of values with potentially different types:

```vary-snippet
let point = (1, 2)
let (x, y) = point         # destructuring
let (a, b, c?) = (1, 2)    # optional element: c is None
```

Tuples support 2 to 8 elements. For larger groupings, use a `data` type.

### Function types

```vary
let transform: (Int) -> Int = lambda x: Int: x * 2
```

### Generics

Classes, data types, and functions can take type parameters:

```vary
data Pair[A, B] {
    first: A
    second: B
}

def identity[T](x: T) -> T {
    return x
}
```

Type arguments are inferred from usage. See [Types](/docs/types/) for more.

## Strings

Strings use double quotes. F-strings embed expressions with `f"..."`:

```vary
let name = "world"
print(f"Hello, {name}!")
print(f"2 + 2 = {2 + 2}")
```

Indexing and slicing:

```vary
let s = "Hello, World!"
print(s[0])         # "H"
print(s[0:5])       # "Hello"
print(s[7:])        # "World!"
```

Common methods: `upper()`, `lower()`, `strip()`, `split(sep)`, `split_once(sep)`, `join(list)`, `replace(old, new)`, `startswith(prefix)`, `endswith(suffix)`, `is_empty()`, `non_empty()`. See [Collections and strings](/docs/collections/) for the full list.

### Raw strings

Prefix a string with `r` to disable escape processing. Useful for regex patterns and file paths:

```vary
let pattern = r"\d+\.\d+"
let path = r"C:\Users\name\docs"
```

Both `r"..."` and `r'...'` are supported.

### Triple-quoted strings

Use `"""..."""` for multiline strings. Triple-quoted strings support escape sequences and automatically strip common leading indentation:

```vary
let sql = """
    SELECT name, age
    FROM users
    WHERE active = true
    """
# Result: "SELECT name, age\nFROM users\nWHERE active = true"
```

Auto-dedent rules: the leading newline after `"""`, the trailing newline before closing `"""`, and the common leading whitespace of all non-empty lines are removed. Escape sequences (`\n`, `\t`, `\\`, etc.) work inside triple-quoted strings.
