Getting Started
Silt is a statically-typed, expression-based language with 14 keywords, full immutability, and CSP-style concurrency. Pattern matching is the only way to branch. Types are inferred. Errors are values.
This guide walks you through the essentials. For the complete reference, see the Language Guide.
Install
curl -fsSL https://silt-lang.com/install.sh | sh
Or build from source:
git clone https://github.com/rendro/silt.git
cd silt && cargo build --release
cp target/release/silt ~/.local/bin/
Your first program
silt init
silt run main.silt
silt init creates a starter main.silt. silt run executes it. That’s the whole loop.
1. Bindings
Everything is immutable. let binds a name to a value. You can shadow, but you can’t reassign.
let x = 42
let x = x + 1 -- shadows, x is now 43
2. Functions
fn add(a, b) {
a + b
}
The last expression is the return value. No return keyword needed (though it exists for early exits).
Anonymous functions:
let double = fn(x) { x * 2 }
3. Pattern matching
The only branching construct. Match on constructors, tuples, lists, records, guards, ranges, or-patterns.
fn describe(n) {
match n {
0 -> "zero"
1 | 2 | 3 -> "small"
_ when n < 0 -> "negative"
_ -> "big"
}
}
Match destructures:
let (x, y) = (1, 2)
match items {
[] -> "empty"
[head, ..tail] -> "non-empty"
}
4. Types
Types are inferred. You only write them for declarations:
type User {
name: String,
age: Int,
}
type Shape {
Circle(Float),
Rect(Float, Float),
}
fn area(shape) {
match shape {
Circle(r) -> 3.14 * r * r
Rect(w, h) -> w * h
}
}
5. Errors as values
No exceptions. Fallible functions return Result. The ? operator propagates errors:
fn read_config(path) {
let content = io.read_file(path)?
let config = json.parse(Config, content)?
Ok(config)
}
Use match to handle the result:
match read_config("app.json") {
Ok(cfg) -> println("loaded: {cfg.name}")
Err(e) -> println("error: {e}")
}
6. Pipes and trailing closures
The |> operator passes the left value as the first argument of the right:
[1, 2, 3, 4, 5]
|> list.filter { n -> n > 2 }
|> list.map { n -> n * n }
|> list.fold(0) { acc, n -> acc + n }
7. Concurrency
Spawn tasks that run in parallel. Communicate through channels.
fn main() {
let ch = channel.new(10)
let worker = task.spawn(fn() {
channel.each(ch) { msg ->
println("got: {msg}")
}
})
channel.send(ch, "hello")
channel.send(ch, "world")
channel.close(ch)
task.join(worker)
}
Tooling
silt run <file.silt> -- run a program
silt check <file.silt> -- type-check without running
silt test [path] -- run test functions
silt fmt <file.silt> -- format source code
silt repl -- interactive REPL
silt lsp -- start the language server
What’s next
- Language Guide — complete coverage of every feature
- Standard Library — all modules and functions
- Concurrency — the full CSP model, channels, and select
- FFI Guide — embed silt in Rust applications
- Editor Setup — configure your editor for silt