A binding gives a value a local name. Atoll uses := when the compiler infers
the type and : plus = when the source states the type.
name := "Ada"
attempts: int = 3Bindings are immutable unless they begin with mut.
Declaration forms
| Form | Type | Initial value | Reassignable |
|---|---|---|---|
name := value |
Inferred | Required | No |
mut name := value |
Inferred | Required | Yes |
name: T = value |
Written as T |
Explicit | No |
mut name: T = value |
Written as T |
Explicit | Yes |
name: T |
Written as T |
Type-defined zero/default | No |
mut name: T |
Written as T |
Type-defined zero/default | Yes |
:= introduces an inferred binding. = either initializes an annotated
binding or assigns to an existing mutable place. Keeping those spellings
distinct lets the parser and the reader see whether a name is new.
count := 0
mut total: int = 0
total = total + countWriting count = 0 for a new inferred binding is an error. Writing
count: int := 0 is also an error; annotated bindings use =.
Immutable by default
An immutable binding cannot be assigned a second value.
name := "Ada"
name = "Grace" // error: `name` is immutableImmutability belongs to the binding. Whether an operation can mutate the value also depends on the value’s type and the receiver or parameter through which it is accessed.
Mutable bindings
Use mut when an algorithm must update a local value.
fn sum(values: []int): int {
mut total := 0
for value in values {
total += value
}
return total
}The mut marker is part of the binding or parameter declaration:
fn increment(mut value: int): int {
value += 1
return value
}Type inference and annotations
An initializer normally supplies enough information:
enabled := true
port := 8080
message := "ready"
coordinates := (12, 34)Use an annotation when the intended type cannot be recovered from the value or when it is part of the contract you want readers to see.
port: u16 = 8080
names: []string = []
result: Option[int] = NoneAtoll’s scalar types use lowercase names such as int, bool, and string.
Nominal composite types use PascalCase, such as Option, Result, List,
and user-defined structs and enums.
Default initialization
An annotated binding may omit = value:
mut total: int
mut names: []stringThe type determines the initial zero/default value. This form requires an
explicit type; an inferred binding always requires := value. Prefer an
explicit initializer when it communicates intent better than the type’s
default.
Destructuring
A pattern can introduce more than one binding.
(left, right) := (10, 20)
first, second := (1, 2)Patterns also appear in for, match, and refutable-binding else forms.
Tuple, struct, variant, and slice patterns share the same pattern language; the
patterns specification will define irrefutability and
failure behavior in one place.
Refutable bindings with else
When a pattern might not match, an else block handles failure. The block must
diverge, for example by returning from the function.
Some(value) := maybe_value else {
return 0
}
return valueBindings introduced by the pattern remain available after the else.
Shadowing and scope
A later declaration may shadow an earlier name. Shadowing creates a new binding; it does not mutate the old one.
input := "42"
input := parse_int(input)Each binding is visible from its declaration to the end of its block, except where a nested or later binding shadows it. Atoll does not hoist local bindings.
Naming
Local variables and parameters use snake_case.
retry_count := 0
fn reserve(seat_count: int): bool {
return seat_count > 0
}See the naming conventions for the complete, current naming table.