Atoll checks every expression statically. Type inference removes redundant annotations, but it does not defer ordinary type errors until runtime.
answer := 42
name := "Atoll"
enabled := trueThe inferred types are int, string, and bool.
Expected types
Inference flows from an initializer into a binding and from an annotated context back into an expression.
port: u16 = 8080
names: []string = []
selected: string? = NoneThe annotation tells the checker how to type an otherwise ambiguous numeric, empty, or absent value.
Expected types also flow through function arguments, return positions, struct fields, collection elements, and branch joins.
fn accepts_byte(value: u8): void {}
accepts_byte(200)The literal is checked as u8; the call does not first create an int and
then apply an arbitrary implicit conversion.
Empty collections
An empty list needs a constraint eventually.
names: []string = []Later operations can sometimes provide that constraint.
mut values := []
values.add(42)If no use establishes the element type, the compiler reports an ambiguous empty-list literal rather than inventing a universal element type.
Numeric literals
Unsuffixed integer and floating literals use context where possible and
otherwise default to int and float.
small: u8 = 12
ordinary := 12
ratio := 0.5A suffix pins a fixed-width type. The compiler checks range and category.
mask := 0xFFu16
precise := 1.25f64An integer literal is not accepted in a floating-only position merely because its mathematical value is representable. Use a floating literal or explicit conversion.
Option lifting
An expected Option[T] can lift a compatible bare T to Some.
name: string? = "Atoll"None still needs an expected optional type. The same expected-type mechanism
supports explicit Some, Ok, and Err construction without erasing their
generic payload information.
Branches
The checker joins the normally completing branches of if and match.
value := if ready {
1
} else {
2
}Compatible branches keep one type. Branches with different closed value types can infer an anonymous union where permitted.
fn choose(as_text: bool) {
if as_text { "one" } else { 1 }
}An early return, propagated error, break, or other diverging branch does
not contribute a normal result type.
Returns
Function parameters always have declared types. Return types can be explicit or inferred.
fn square(value: int): int => value * value
fn inferred(value: int) {
return value * value
}The compiler collects explicit returns and tail expressions. Recursive functions need enough annotation or a compatible base case for inference to reach a stable result.
For exported APIs, write the return type. It records intent and prevents an internal edit from changing the callable contract.
Coercions
Atoll permits specific, checked coercions:
- contextual numeric literals;
- bare values into expected
Option/Resultvariants; - concrete values into an expected anonymous union;
- values satisfying an
impl Traitboundary; - representation-preserving cases explicitly registered by the type system.
It does not provide a universal “convert anything that fits” rule. Runtime
conversions use methods or as where supported.
Annotations
Add an annotation when:
- a literal lacks enough context;
- a public contract should remain stable;
- an empty or absent value is ambiguous;
- a numeric width is externally significant;
- several union alternatives should be intentionally constrained;
- an inferred error or effect boundary should be explicit.
Avoid repeating a type when the initializer already makes it obvious and no boundary depends on the spelling.
Diagnostics
A mismatch diagnostic identifies the expected and actual types and attaches blame to the annotation, argument, field, branch, or return that introduced the constraint.
count: int = "many" // errorInference never changes runtime semantics to make a mismatch disappear. Fix the expression, state the intended type, or perform a supported conversion.