Types & Values
Almide is statically typed with full type inference. Every value has a known type at compile time. There are no implicit conversions, no null, and no truthiness.
Primitive types
Section titled “Primitive types”| Type | Description | Examples |
|---|---|---|
Int | 64-bit signed integer | 42, 0xFF, 1_000_000 |
Float | 64-bit floating point | 3.14, 1.0e-3 |
String | UTF-8 text | "hello", 'raw', """heredoc""" |
Bool | Boolean | true, false |
Unit | No meaningful value | () |
Path | File system path | Used by fs module functions |
Bytes | Byte sequence | Used by crypto, fs for binary data |
Numeric literals support _ as a visual separator: 1_000_000, 0xFF_FF.
Strings
Section titled “Strings”Double-quoted strings support interpolation and escape sequences:
let name = "world"let msg = "hello ${name}, 1+1=${1 + 1}" // "hello world, 1+1=2"let escaped = "line1\nline2"Single-quoted strings support escapes (\', \\, \n, \t, \r) but no interpolation:
let s = 'it\'s a string'Raw strings have no escapes and no interpolation:
let pattern = r"^\d+\.\d+$"Heredoc strings strip leading whitespace based on minimum indent:
let sql = """ SELECT * FROM users WHERE active = true"""Raw heredocs (r"""...""") disable both escapes and interpolation.
Boolean values are true and false. Almide uses keyword operators for logic:
let a = true and false // falselet b = true or false // truelet c = not true // falseThere is no && or ||. Use and, or, and not for boolean logic. (The postfix ! is the unwrap operator, not boolean negation.)
Unit represents “no meaningful value.” Its only value is (). Functions that perform side effects and return nothing use Unit:
effect fn log(msg: String) -> Result[Unit, String] = { println(msg) ok(())}Lists are ordered, homogeneous collections:
let xs = [1, 2, 3] // List[Int]let empty: List[String] = [] // empty listlet first = xs[0] // index access: 1Mutable lists support index writes:
var items = ["a", "b", "c"]items[1] = "B" // ["a", "B", "c"]list.push(items, "d") // ["a", "B", "c", "d"]+ concatenates lists:
let combined = [1, 2] + [3, 4] // [1, 2, 3, 4]See the list stdlib for full API.
Maps are key-value collections. Map literals use square brackets with : separating keys and values:
let m = ["name": "Alice", "role": "admin"] // Map[String, String]let empty: Map[String, Int] = [:] // empty mapMap access returns Option[V]:
let name = m["name"] // some("Alice")let age = m["age"] // noneMutable maps support writes:
var scores: Map[String, Int] = [:]scores["alice"] = 100scores["bob"] = 85See the map stdlib for full API.
Sets are unordered collections of unique values:
let s = set.from_list([1, 2, 3, 2]) // Set containing 1, 2, 3set.contains(s, 2) // trueTuples are fixed-size collections of heterogeneous types:
let pair = (1, "hello") // (Int, String)let triple = (true, 42, "ok") // (Bool, Int, String)Access tuple elements with .0, .1, etc.:
let x = pair.0 // 1let y = pair.1 // "hello"Tuples are commonly used for multiple return values and for loop destructuring:
for (i, item) in list.enumerate(items) { println("${int.to_string(i)}: ${item}")}Option
Section titled “Option”Option[T] represents a value that may or may not exist. It replaces null:
let found = some(42) // Option[Int] with a valuelet missing = none // Option[Int] with no valueUse match to handle both cases:
match list.get(xs, 0) { some(x) => println("found: ${int.to_string(x)}"), none => println("empty list"),}See Error Handling for more on Option.
Result
Section titled “Result”Result[T, E] represents a computation that can succeed or fail:
let success = ok(42) // Result[Int, String]let failure = err("not found") // Result[Int, String]Use match to handle both cases:
match int.parse(input) { ok(n) => println("parsed: ${int.to_string(n)}"), err(e) => println("error: ${e}"),}See Error Handling for effect fn, unwrap operators (!, ??, ?), and guard.
Record types
Section titled “Record types”Records are named product types with labeled fields:
type User = { name: String, age: Int, active: Bool,}Create records with the same syntax:
let alice = { name: "Alice", age: 30, active: true }Access fields with .:
println(alice.name) // "Alice"Update records with spread:
let inactive = { ...alice, active: false }Field shorthand works when the variable name matches the field name:
let name = "Bob"let age = 25let bob = { name, age, active: true }Variant types
Section titled “Variant types”Variants (algebraic data types) represent values that can be one of several cases:
type Shape = | Circle(Float) | Rect{ width: Float, height: Float } | PointThree case forms are supported:
| Form | Syntax | Example |
|---|---|---|
| Unit | | CaseName | | Point |
| Tuple | | CaseName(Types...) | | Circle(Float) |
| Record | | CaseName{ fields... } | | Rect{ width: Float, height: Float } |
Use match to destructure variants:
fn area(s: Shape) -> Float = match s { Circle(r) => 3.14159 * r ^ 2, Rect{ width, height } => width * height, Point => 0.0, }Inline variants (without leading |) work for simple enumerations:
type Direction = North | South | East | WestGeneric types
Section titled “Generic types”Types can be parameterized with type variables using []:
type Pair[A, B] = { first: A, second: B,}
let p: Pair[Int, String] = { first: 1, second: "one" }See Generics for details.
Type aliases
Section titled “Type aliases”Type aliases create transparent alternative names for existing types:
type Score = Inttype Handler = (String) -> StringAliases are interchangeable with their underlying type.
Function types
Section titled “Function types”Function types describe callable values:
type Predicate = Fn(Int) -> Booltype Transform = Fn(String) -> StringUsed with higher-order functions:
fn apply(f: Fn(Int) -> Int, x: Int) -> Int = f(x)Deriving conventions
Section titled “Deriving conventions”Types can derive built-in conventions using : after the type name:
type Color: Eq = | Red | Green | BlueThis generates implementations automatically. Available conventions: Eq, Repr, Ord, Hash, Codec. See Protocols for details.
Built-in protocols
Section titled “Built-in protocols”Eq and Hash are automatically derived by the compiler for all value types (except function types). No explicit deriving is needed:
let same = color_a == color_b // just worksSee Protocols for user-defined protocols.
Next steps
Section titled “Next steps”- Variables — binding values with
letandvar - Functions — defining and calling functions
- Pattern Matching — destructuring values with
match