The Ore Programming Language
Ore is a statically-typed, compiled programming language with indentation-based syntax and algebraic effects.
Design Principles
- Immutable by default. Bindings are immutable. Rebinding with
=creates a new shadow, not a mutation. - Public by default. Items are public unless marked
pvt. - Effects are explicit. Side effects are tracked in function signatures with
!(IO + State). - No null. Use
Option<T>for optional values andResult<T, E>for fallible operations. - Pattern-driven. Destructuring and pattern matching are deeply integrated — in
let,match,when, and multi-clause functions. - Functional loops.
loop/recurwith accumulators instead of mutable loop variables. - Algebraic effects for resource management.
handler/withreplace both exceptions and RAII/Drop.
Syntax Overview
Ore uses indentation to delimit blocks. A colon : at the end of a line introduces an indented block:
fn greet(name: String):
let msg = "Hello, " + name
print(msg)
Braces { } are used for struct/enum definitions and struct literals, not for blocks.
A Quick Example
enum Shape {
Circle(f64),
Rectangle(f64, f64),
}
fn area(s: Shape) -> f64:
match s:
Circle(r): 3.14159 * r ** 2
Rectangle(w, h): w * h
fn main() !(IO):
let shapes = [Circle(5.0), Rectangle(3.0, 4.0)]
shapes.for_each(|s| print(area(s).show()))
Literals & Primitive Types
Integers
Integer literals default to i64. Underscores can be used as visual separators.
42
0
9999999
1_000_000
Alternate bases:
0xff // hexadecimal (case-insensitive: 0XAB, 0xDeAdBeEf)
0b1010 // binary (0B1100)
0o77 // octal (0O55)
Integer Types
| Type | Size | Range |
|---|---|---|
i8 | 8-bit | signed |
i16 | 16-bit | signed |
i32 | 32-bit | signed |
i64 | 64-bit | signed |
isize | pointer | signed |
u8 | 8-bit | unsigned |
u16 | 16-bit | unsigned |
u32 | 32-bit | unsigned |
u64 | 64-bit | unsigned |
usize | pointer | unsigned |
Floats
Float literals default to f64. Underscores allowed.
3.14159
0.5
100.0
1_000.5
Scientific notation:
1e10 // 10000000000.0
1.5e10 // 15000000000.0
2.5e-3 // 0.0025
3.0e+7 // 30000000.0
Float Types
| Type | Size |
|---|---|
f32 | 32-bit |
f64 | 64-bit |
Strings
Double-quoted. The type is String.
"hello"
"" // empty
"hello world" // spaces
Escape sequences:
| Escape | Meaning |
|---|---|
\n | newline |
\t | tab |
\r | carriage return |
\" | double quote |
\\ | backslash |
\0 | null byte |
\' | single quote |
Triple-quoted strings for raw content:
"""triple quoted"""
Booleans
true
false
The type is bool.
Bytes
Single-quoted character literals produce a u8 value:
'a' // 97
'z'
'0'
' ' // space
Escape sequences work in byte literals too:
'\n' // newline byte
'\t' // tab byte
'\\' // backslash byte
'\'' // single quote byte
'\0' // null byte
Variables & Bindings
Let Bindings
Variables are introduced with let. They are immutable by default.
let x = 5
let name = "Alice"
Optional type annotation:
let x: i64 = 5
Bindings can use any expression:
let x = 5 + 3
let y = x * 2
let result = compute(42)
let s = "hello".to_upper()
Destructuring
Tuples:
let (a, b) = get_pair()
let (a, (b, c)) = get_nested()
let (_, b) = get_pair() // wildcard discards first element
Structs:
let Point { x, y } = get_point()
let Point { x: px, y: py } = get_point() // rename fields
let Config { name, .. } = get_config() // ignore remaining fields
Rebinding
The = operator rebinds a name to a new value. This is shadowing, not mutation:
fn count() -> i64:
let x = 0
x = x + 1 // x is now 1 (new binding, not mutation)
x = x + 2
x // 3
Compound assignment operators desugar to rebinding (see Operators):
fn count() -> i64:
let x = 0
x += 1
x += 2
x // 3
Field rebinding:
let p = Point::new(1, 2)
p.x = 10 // creates new binding with updated field
Constants
Top-level compile-time values with required type annotations:
const MAX_SIZE: i64 = 1024
const PI: f64 = 3.14159
const GREETING: String = "hello"
const YES: bool = true
const BYTE_A: u8 = 'a'
Constants support all literal forms:
const HEX: i64 = 0xFF
const BIN: i64 = 0b1010
const OCT: i64 = 0o77
const SCI: f64 = 1.5e10
const TRIPLE: String = """triple quoted"""
Visibility and attributes:
pvt const SECRET: i64 = 42
#[deprecated]
const OLD_VALUE: i64 = 0
Functions
Basic Functions
Functions are declared with fn. The body follows a colon and is indented:
fn add(x: i64, y: i64) -> i64:
x + y
No return type means the function returns unit:
fn greet(name: String):
print(name)
Short functions can be written inline:
fn double(x: i64) -> i64: x * 2
fn apply(x: i64) -> i64: compute(x)
Generics
Type parameters go in angle brackets after the function name:
fn identity<T>(x: T) -> T:
x
With trait bounds:
fn print_it<T: Display>(x: T):
print(x)
With where clauses:
fn convert<T, U>(x: T) -> U where T: Into<U>:
x.into()
Effects
Functions that perform side effects declare them with !(...) after the return type:
fn read_file(path: String) -> String !(IO):
// ...
fn complex(x: i64) -> String !(IO + State):
// ...
See Effects & Handlers for the full effect system.
Multi-Clause Functions
Functions can pattern-match on their arguments using multiple clauses. The body contains (patterns): body clauses, similar to match:
fn fib(n: i64) -> i64:
(0): 0
(1): 1
(n): fib(n - 1) + fib(n - 2)
Each clause lists patterns for the arguments in parentheses.
Function Type Parameters
Functions can accept other functions as parameters using fn(Args) -> Ret type syntax:
fn apply(f: fn(i64) -> i64, x: i64) -> i64:
f(x)
Visibility and Attributes
Functions are public by default. Use pvt for private:
pvt fn helper() -> i64:
42
Attributes precede the function:
#[inline]
fn fast(x: i64) -> i64:
x * 2
Everything Combined
#[some_attr]
pvt fn kitchen_sink<T, U: Default>(x: T, y: U) -> Result<T, U> !(IO + State) where T: Display + Clone:
// ...
Lambdas
Anonymous functions use pipe syntax:
let f = |x| x + 1
let add = |x, y| x + y
let constant = || 42
Lambdas are commonly used with higher-order functions:
items.map(|x| x * 2)
items.filter(|x| x > 0)
items.fold(0, |acc, x| acc + x)
Calling Functions
Standard call syntax:
add(5, 3)
f(g(x))
f(x + y, y * 2)
Method calls on values:
x.len()
x.concat(y)
x.trim().len() // chaining
Path-qualified calls:
Foo::new()
std::io::File::open("test.txt")
Operators
Arithmetic
| Operator | Meaning | Example |
|---|---|---|
+ | addition | x + y |
- | subtraction | x - y |
* | multiplication | x * y |
/ | division | x / y |
% | modulo | x % y |
** | exponentiation | x ** y |
Comparison
| Operator | Meaning | Example |
|---|---|---|
== | equal | x == y |
!= | not equal | x != y |
< | less than | x < y |
<= | less than or equal | x <= y |
> | greater than | x > y |
>= | greater than or equal | x >= y |
Logical
| Operator | Meaning | Example |
|---|---|---|
and | logical AND (short-circuit) | x and y |
or | logical OR (short-circuit) | x or y |
not | logical NOT | not x |
and and or are keywords, not symbols. They short-circuit: the right operand is only evaluated if needed.
Bitwise
| Operator | Meaning | Example |
|---|---|---|
& | bitwise AND | x & y |
^ | bitwise XOR | x ^ y |
~ | bitwise NOT | ~x |
<< | left shift | x << y |
>> | right shift | x >> y |
Unary
| Operator | Meaning | Example |
|---|---|---|
- | negation | -x |
not | logical NOT | not x |
~ | bitwise NOT | ~x |
Precedence
From highest to lowest binding power:
| Precedence | Operators | Associativity |
|---|---|---|
| 25 | . () ? | left (postfix) |
| 21 | - not ~ (unary) | prefix |
| 19 | ** | right |
| 17 | * / % | left |
| 15 | + - | left |
| 13 | << >> | left |
| 11 | & | left |
| 9 | ^ | left |
| 7 | == != < <= > >= | left |
| 5 | and | left |
| 3 | or | left |
| 1 | = += -= *= /= %= | right |
Parentheses override precedence:
x + y * z // y * z first
(x + y) * z // x + y first
-a + b ** 2 // (-a) + (b ** 2)
Compound Assignment
| Operator | Desugars to |
|---|---|
x += y | x = x + y |
x -= y | x = x - y |
x *= y | x = x * y |
x /= y | x = x / y |
x %= y | x = x % y |
Works on fields too:
p.x += 10 // p = Point { x: p.x + 10, ..p }
lexer.pos += len
Try Operator
The postfix ? operator propagates errors from Result values:
fn read_config(path: String) -> Result<Config, Error>:
let contents = read_file(path)? // returns Err early if read fails
let config = parse(contents)?
Ok(config)
? desugars to a match on the Try trait — on Err, it returns the error; on Ok, it unwraps the value.
Chaining:
fn combine(x: Result<i64, String>, y: Result<i64, String>) -> Result<i64, String>:
Ok(x? + y?)
Types
Structs
Unit Structs
No fields:
struct Unit
pvt struct PvtUnit
Newtype / Tuple Structs
Positional fields in parentheses:
struct Newtype(i64)
struct Pair(i64, i64)
Fields can have visibility:
struct TupleVis(pvt i64, String)
Record Structs
Named fields in braces:
struct Point {
x: i64,
y: i64,
}
Field visibility:
struct RecordVis {
pvt name: String,
age: i64,
}
Generics
Type parameters with optional bounds:
struct Container<T> {
value: T,
}
struct Bounded<T: Clone + Debug> {
value: T,
}
struct Multi<T: Clone, U: Display> {
first: T,
second: U,
}
Where Clauses
Complex bounds can use where:
struct WithWhere<T, U> where T: Clone + Debug, U: Display {
first: T,
second: U,
}
struct BoundedTuple<T>(T) where T: Clone
Attributes
#[derive(Debug, Clone)]
struct Annotated {
field: i64,
}
#[derive(Debug)]
#[repr(C)]
struct CRepr {
x: i32,
y: i32,
}
Multiple attributes can be stacked.
Everything Combined
#[derive(Debug, Clone, PartialEq)]
pvt struct FullBellsAndWhistles<T, U: Default> where T: Clone + Debug {
pvt id: i64,
name: String,
data: T,
extra: U,
}
Recursive Structs
Use Box for indirection:
struct List<T> {
head: T,
tail: Option<Box<List<T>>>,
}
Struct Literals
Creating struct values:
Point { x: 1, y: 2 }
Shorthand when variable names match fields:
let x = 1
let y = 2
Point { x, y } // same as Point { x: x, y: y }
Spread to copy remaining fields:
let p = Point { x: 1, y: 2 }
Point { x: 10, ..p } // y comes from p
Path-qualified:
std::geo::Point { x: 1, y: 2 }
Nested:
Line { start: Point { x: 0, y: 0 }, end: Point { x: 1, y: 1 } }
Enums
Unit Variants
C-style enumerations:
enum Color { Red, Green, Blue }
pvt enum Internal { A, B }
Tuple Variants
Variants carrying unnamed data:
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
Record Variants
Variants with named fields:
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
}
Mixed Variants
An enum can mix unit, tuple, and record variants:
enum Message {
Quit,
Move { x: i64, y: i64 },
Write(String),
ChangeColor(i64, i64, i64),
}
Generics, Bounds, and Where Clauses
enum Wrapper<T: Clone> {
Item(T),
Empty,
}
enum Constrained<T, U> where T: Clone, U: Debug {
Both(T, U),
First(T),
}
Recursive Enums
enum Tree<T> {
Leaf(T),
Node(Box<Tree<T>>, Box<Tree<T>>),
}
GADTs
Variants can specialize their return type with ->:
enum Expr<T> {
LitInt(i64) -> Expr<i64>,
LitBool(bool) -> Expr<bool>,
Add(Expr<i64>, Expr<i64>) -> Expr<i64>,
If(Expr<bool>, Expr<T>, Expr<T>) -> Expr<T>,
Eq(Expr<i64>, Expr<i64>) -> Expr<bool>,
}
This enables type-safe expression trees where the type parameter tracks the result type of each node.
Type Aliases
type Byte = i8
type Result<T> = std::result::Result<T, MyError>
type Pair<A, B> = Tuple2<A, B>
type CloneVec<T: Clone> = Vec<T>
type Mapped<T, U> = Map<T, U> where T: Clone, U: From<T>
pvt type Internal = InternalImpl
Tuples
Ordered, fixed-size groups of values:
(1, 2)
(42, "hello", true)
(1, (2, 3)) // nested
Arrays
Fixed-size, homogeneous collections:
[1, 2, 3]
[] // empty
["hello", "world"]
[[1, 2], [3, 4]] // nested
Array types are written [T; n]:
fn make_array() -> [i64; 3]:
[1, 2, 3]
Traits & Impls
Traits
Marker Traits
Traits with no methods:
trait Marker
Methods
trait Greet:
fn hello(self) -> String
fn goodbye(self) -> String
Default Implementations
Methods can have a default body:
trait WithDefaults:
fn required(self) -> i64
fn optional(self) -> String:
"default"
Associated Types
Types declared inside a trait, defined by each impl:
trait Iterator:
type Item
fn next(self) -> Option<Self::Item>
Associated types can have bounds:
trait Collection:
type Item: Clone + Debug
fn first(self) -> Self::Item
Associated Constants
trait HasSize:
const SIZE: i64
Generic Traits
trait Into<T>:
fn into(self) -> T
Supertraits
A trait can require other traits with with:
trait Sortable with Ord:
fn sort(self) -> Self
trait Printable with Display + Debug:
fn print(self) -> String
Where Clauses
trait Convert<T, U> where T: Clone, U: From<T>:
fn convert(self, value: T) -> U
Generic Methods
Methods can have their own type parameters:
trait Mapper:
fn map<U>(self, f: fn(Self) -> U) -> U
Static Methods
Methods without self are static (called on the type, not an instance):
trait Factory:
fn create() -> Self
fn default() -> Self
Visibility and Attributes
pvt trait InternalTrait:
fn internal(self) -> i64
#[some_attr]
trait Documented:
fn describe(self) -> String
Everything Combined
#[some_attr]
pvt trait FullTrait<T, U: Default> with Clone + Debug where T: Display:
type Output: Clone
const MAX: i64
fn required(self, input: T) -> Self::Output
fn optional(self) -> U:
U::default()
fn static_method() -> Self
fn generic<V>(self, v: V) -> V where V: Into<T>
Impls
Inherent Impls
Methods on a type (no trait):
impl Point:
fn new(x: i64, y: i64) -> Point:
// ...
fn distance(self) -> f64:
// ...
Trait Impls
Implementing a trait for a type:
impl Display for Point:
fn display(self) -> String:
// ...
Generic Impls
impl<T> Container<T>:
fn unwrap(self) -> T:
// ...
impl<T: Clone> Clone for Container<T>:
fn clone(self) -> Container<T>:
// ...
Blanket Impls
Implement a trait for all types matching a bound:
impl<T> Show for T where T: Display:
fn show(self) -> String:
// ...
Multi-Clause Methods
Pattern matching on self in impl methods:
impl<T, E> Result<T, E>:
fn is_ok(self) -> bool:
(Ok(_)): true
(Err(_)): false
The body contains (patterns): body clauses, like a match on the arguments.
Associated Type and Const Impls
impl Iterator for Counter:
type Item = i64
fn next(self) -> Option<i64>:
// ...
impl HasSize for Point:
const SIZE: i64 = 2
Private Methods and Impls
impl Point:
fn public_method(self) -> i64:
// ...
pvt fn helper(self) -> i64:
// ...
pvt impl InternalTrait for Point:
fn internal(self) -> i64:
// ...
Everything Combined
#[some_attr]
pvt impl<T, U: Default> FullTrait<T, U> for FullBellsAndWhistles<T, U> where T: Display:
type Output = T
const MAX: i64 = 100
fn required(self, input: T) -> T:
// ...
fn optional(self) -> U:
// ...
fn static_method() -> Self:
// ...
fn generic<V>(self, v: V) -> V where V: Into<T>:
// ...
Control Flow
Ore has no if/else. Instead, it uses when for conditionals, match for pattern matching, and loop/recur for iteration.
When
Multi-Arm Conditional
when: with indented arms replaces if-else chains. Use _ for the default case:
fn classify(x: i64) -> String:
when:
x > 0: "positive"
x < 0: "negative"
_: "zero"
Arms with multiple statements:
fn process(x: i64) -> i64:
when:
x > 0:
let doubled = x * 2
doubled + 1
_: 0
Single Guard
A standalone when acts as an early exit or guard:
fn clamp(x: i64) -> i64:
when x > 100: return 100
when x < 0: return 0
x
Match
Pattern matching on a value:
fn describe(opt: Option<i64>) -> String:
match opt:
Some(v): v.show()
_: "nothing"
Literal Patterns
match x:
0: "zero"
1: "one"
_: "other"
Enum Patterns
match result:
Ok(v): v
Err(msg): -1
Or Patterns
Multiple patterns separated by |:
match x:
1 | 2 | 3: "small"
4 | 5 | 6: "medium"
_: "large"
Range Patterns
Exclusive ranges with ..:
match x:
0..10: "single digit"
10..100: "double digit"
_: "big"
Guards
Add conditions with when:
match opt:
Some(x) when x > 0: "positive"
Some(x) when x < 0: "negative"
Some(_): "zero"
_: "none"
Nested Patterns
match x:
Some(Some(v)): v
Some(_): -1
_: -2
Struct Patterns
match p:
Point { x: 0, y: 0 }: "origin"
Point { x, y }: "other"
Multi-Statement Arms
match x:
0:
let result = compute()
result + 1
_: x
Loop and Recur
Ore uses functional loops. loop creates a loop with named accumulators; recur jumps back with new values.
Accumulator Loop
fn sum_to_ten() -> i64:
loop (i = 0, sum = 0):
when i >= 10: sum
recur(i + 1, sum + i)
The loop binds i and sum to initial values. recur(...) restarts the loop with new values. When a non-recur expression is reached, the loop returns that value.
Single Binding
fn double_until() -> i64:
loop (x = 1):
when x >= 100: x
recur(x * 2)
Complex Loops
Loops can contain any expression, including match:
fn find(items: Vector<i64>, target: i64) -> Option<i64>:
loop (i = 0):
when i >= items.len(): None
match items.get(i):
Some(v) when v == target: Some(i)
_: recur(i + 1)
Collecting
Build up results with an accumulator:
fn squares() -> Vector<i64>:
loop (i = 0, result = Vector::new()):
when i >= 5: result
recur(i + 1, result.push(i * i))
Bare Loop
Infinite loop with explicit return:
fn repl() -> i64:
loop:
let line = read_line()
when line == "quit": return 0
process(line)
While-Style Loop
A condition after loop is checked each iteration:
fn process_connection(conn: Connection) -> i64:
loop conn.is_alive():
process(conn)
Return
return exits the function early with a value:
fn early_exit(x: i64) -> i64:
when x < 0: return -1
x * 2
Effects & Handlers
Ore uses algebraic effects for side effects, resource management, and control flow. The system has three constructs:
effect— declares an effect and its operationshandler— defines an implementation of an effectwith— installs a handler for a scope
Declaring Effects
An effect declares a set of operations that code can perform:
effect State<S>:
fn get() -> S
fn set(s: S)
Operation Kinds
There are three kinds of effect operations:
| Kind | Meaning | Continuation |
|---|---|---|
fn | Tail-resumptive: always resumes exactly once | No continuation captured (cheap) |
ctl | Control: may resume zero or many times | Captures continuation |
final | Final: never resumes | No continuation (like exceptions) |
effect Async<T>:
fn spawn(task: fn() -> T) -> T // always resumes
ctl yield() -> T // may suspend/resume
ctl abort(reason: String) -> Never // may not resume
effect Error:
final throw(msg: String) -> Never // never resumes
fn is the cheapest — use it when the handler always returns a value and continues. ctl captures a continuation, which the handler can call to resume. final is an optimization hint: the compiler knows no continuation is needed.
Marker Effects
Effects with no operations:
effect Pure
Super Effects
An effect can extend others with with:
effect FileIO with IO:
fn read_file(path: String) -> String
fn write_file(path: String, content: String)
effect NetworkIO with IO + Exception:
fn fetch(url: String) -> String
Generics, Bounds, and Where Clauses
effect Transform<T: Clone>:
fn transform(value: T) -> T
effect Convert<T, U> where T: Clone, U: From<T>:
fn convert(value: T) -> U
Visibility and Attributes
pvt effect InternalEffect:
fn internal_op() -> i64
#[some_attr]
effect Documented:
fn describe() -> String
Everything Combined
#[some_attr]
pvt effect FullEffect<T, U: Default> with IO + Exception where T: Clone:
fn required(value: T) -> U
ctl fail(msg: String) -> Never
final panic(msg: String) -> Never
Declaring Effects on Functions
Functions declare which effects they perform with !(...):
fn read_file(path: String) -> String !(IO):
// ...
fn complex(x: i64) -> String !(IO + State):
// ...
Handlers
A handler expression defines how to handle an effect’s operations. Operation parameters are just names (types come from the effect definition):
fn simple_handler():
handler State:
get(): state
set(s): state = s
Return Clause
Process the final value of the handled computation:
fn state_handler(init: i64):
state = init
handler State:
get(): state
set(s): state = s
return(val): val
Initially and Finally
Resource management without a Drop trait:
fn file_handler(path: String):
let fd = Os::open(path)
handler File:
initially: print("opening")
finally: Os::close(fd)
read(buf, len): Os::read(fd, buf, len)
write(data): Os::write(fd, data)
initially runs when the handler is installed. finally runs when the handled scope exits (whether normally or via a final operation).
All Clauses
fn full_handler():
let count = 0
handler Logger:
initially: print("logger started")
finally: print("logger stopped")
log(msg):
count = count + 1
print(msg)
get_count(): count
return(val): (val, count)
Resume
In ctl handlers, resume(value) continues the captured continuation with a value:
fn example() -> i64:
with handler Ask:
ask():
resume(42)
Ask::ask() + 1 // ask() resumes with 42, so this returns 43
Not calling resume short-circuits — the handler’s value becomes the result of the entire with block:
fn safe_div(x: i64, y: i64) -> i64:
with handler Fail:
fail(msg):
-1 // no resume → with block returns -1
when y == 0:
Fail::fail("division by zero")
x / y
resume can be called multiple times for effects like non-determinism:
fn example() -> List<i64>:
with handler Choose:
choose():
let a = resume(true)
let b = resume(false)
a ++ b
let x = when Choose::choose(): 1 else: 2
[x]
fn operations auto-resume (no explicit resume needed). final operations never resume.
With Expressions
with installs a handler for a block of code.
Scoped Body
fn example() -> i64:
let (state, x) = with state_handler(0):
State::set(42)
State::get()
x + 1
Rest of Scope
Without a : body, the handler covers the rest of the enclosing scope:
fn example() -> i64:
with state(0)
State::set(10)
State::get()
Nested Handlers
fn example() -> i64:
with diagnostic_stderr():
with state_handler(0):
State::set(42)
State::get()
Inline Handler
Define and install a handler in one expression:
fn example() -> i64:
let x = with handler State:
get(): 42
set(s): state = s
x
Single-Operation Shorthand
For handlers with a single operation, skip the handler keyword — the effect is inferred from the operation name:
fn example() -> i64:
with ctl fail(msg):
-1
safe_div(10, 0)
fn example2() -> i64:
with ctl ask():
resume(42)
Ask::ask() + 1
fn example3() -> i64:
with fn get():
42
State::get()
This is equivalent to with handler Effect: op(params): body but more concise for single-operation cases.
Modules & Imports
Modules
File-Based Modules
Declare a module that maps to a file:
mod utils
This looks for utils.ore in the same directory.
Inline Modules
Define a module’s contents inline:
mod math:
fn add(x: i64, y: i64) -> i64:
x + y
Inline modules can contain any items (structs, functions, nested modules, etc.):
pvt mod helpers:
struct Helper
fn assist() -> i64:
42
Visibility and Attributes
pvt mod internal
#[cfg(test)]
mod tests:
fn test_add() -> bool:
true
Imports
Simple Path
Import a module:
use std::io
Specific Item
use std::io::File
Glob Import
Import everything from a module:
use std::io::*
Aliases
Rename on import:
use std::io as sio
use std::io::File as F
Tree Imports
Import multiple items from the same path:
use std::io::{File, BufReader}
Nested trees:
use std::{io::{File, BufReader}, collections::HashMap}
Aliases inside trees:
use std::io::{File, Write as W}
Special Path Segments
| Segment | Meaning |
|---|---|
super | Parent module |
ignot | Package/crate root |
self | Current module |
use super::something
use super::super::deep
use ignot::parser::ast::Item
use std::io::{self, File}
Private Imports
pvt use std::io::File
Intrinsics
Intrinsics are compiler-builtin operations prefixed with @. They bypass normal name resolution and map directly to low-level operations.
Syntax
@name(args)
Examples:
@add(x, y)
@nop()
@fma(a, b, c)
Purpose
Intrinsics exist for operations that cannot be expressed in Ore itself:
- Arithmetic primitives —
@add<T>,@sub<T>,@mul<T>, etc. (used to implement operator traits) - Memory operations —
@alloc,@free,@load<T>,@store<T>,@copy - System calls —
@write,@read,@open,@close,@exit - Type operations —
@sizeof<T>,@as<To>(casting) - Process control —
@panic,@exit,@arg_count,@arg
Regular Ore code rarely uses intrinsics directly. They are primarily used in the standard library to implement traits and effects.
Keyword Reference
| Keyword | Usage |
|---|---|
and | Logical AND (short-circuit): x and y |
const | Compile-time constant: const N: i64 = 42 |
ctl | Control effect operation (captures continuation) |
effect | Declare an algebraic effect |
enum | Declare an enumeration type |
false | Boolean literal |
final | Final effect operation (never resumes) |
fn | Declare a function, or a tail-resumptive effect operation |
for | Trait implementation target: impl Trait for Type |
handler | Define an effect handler |
ignot | Package/crate root in paths |
impl | Implement methods or traits for a type |
let | Bind a value to a name |
loop | Functional loop with accumulators |
match | Pattern matching on a value |
mod | Declare a module |
not | Logical NOT: not x |
or | Logical OR (short-circuit): x or y |
pvt | Mark an item as private (public by default) |
recur | Jump back to enclosing loop with new values |
resume | Resume continuation in ctl handler: resume(value) |
return | Return a value from a function early |
Self | The implementing type in traits/impls |
self | The receiver in methods, or current module in paths |
struct | Declare a struct type |
super | Parent module in paths |
trait | Declare a trait |
true | Boolean literal |
type | Declare a type alias |
use | Import items from a module |
when | Conditional expression or match guard |
where | Introduce type bounds on generic parameters |
with | Install an effect handler, or declare supertraits/supereffects |
Reserved Symbols
| Symbol | Usage |
|---|---|
@ | Intrinsic prefix: @add(x, y) |
? | Try/error propagation: expr? |
#[...] | Attribute annotation |
!(...) | Effect declaration on functions: !(IO + State) |
|...| | Lambda parameters: |x| x + 1 |
:: | Path separator: Foo::bar |
.. | Spread in struct literals, range in patterns |
-> | Return type separator |
_ | Wildcard pattern |