Type Inference and Abstractions
You shouldn't have to spell out every type. The compiler can figure out most types from context. But you also shouldn't have to guess what types are flowing through your code. We want the best of both worlds: less boilerplate, but full transparency.
Type Inference
We infer types locally but require annotations at boundaries:
// Function signature requires types
fn process($input string): Result<i32, Error> {
// Inside, types are inferred
$trimmed = $input.trim(); // inferred as string
$parsed = $trimmed.parse(); // inferred as Result<i32, ParseError>
$value = $parsed?; // inferred as i32
$value * 2 // return type matches signature
}
Why This Works
- Reading code: Function signatures tell you what goes in and out
- Writing code: Less boilerplate inside functions
- Clarity: Types at boundaries, inference for local variables
- Transparency: You always know what types cross abstraction boundaries
Inference Rules
The compiler infers types from:
- Literal values:
$x = 42; // inferred as int (default integer type)
$y = 3.14; // inferred as float (default float type)
$s = "hello"; // inferred as string
- Function calls:
fn add($a i32, $b i32): i32 { $a + $b }
$result = add(10, 20); // inferred as i32 from return type
- Method calls:
$s = "hello";
$len = $s.len(); // inferred as usize from method signature
- Operators:
$x = 10; // inferred as i32
$y = $x + 5; // inferred as i32 because $x is i32
Explicit Annotations When Needed
Sometimes the compiler needs help:
// Ambiguous without annotation
$numbers = vec![1, 2, 3]; // ERROR: could be Vec<i32>, Vec<i64>, etc.
// Explicit type resolves ambiguity
$numbers Vec<i32> = vec![1, 2, 3]; // Clear!
// Or turbofish syntax
$numbers = vec::<i32>[1, 2, 3];
Zero-Cost Abstractions
High-level code should compile to efficient machine code. No runtime overhead for abstractions.
Generics
Generics compile to monomorphized code — a separate copy for each concrete type:
fn swap<T>($a &T, $b &T) {
$temp = *$a;
$a = $b;
*$b = $temp;
}
// Compiling swap::<i32> generates code as efficient as:
fn swap_i32($a &i32, $b &i32) {
$temp = *$a;
$a = $b;
*$b = $temp;
}
No runtime dispatch, no indirection, no overhead. The abstraction costs nothing.
Iterators
Iterator chains compile to tight loops:
$sum = $numbers
.iter()
.filter(($x) { *$x > 0 })
.map(($x) { $x * 2 })
.sum();
// Compiles to code equivalent to:
$sum = 0;
for $x in $numbers {
if $x > 0 {
$sum += $x * 2;
}
}
The high-level iterator chain is just as fast as a hand-written loop.
Inline Functions
Small functions inline automatically:
fn square($x i32): i32 {
$x * $x
}
$result = square(5);
// Compiles to:
$result = 5 * 5;
No function call overhead. The abstraction disappears at compile time.
Traits: Shared Behavior
Traits define shared behavior across types. Like interfaces, but more powerful.
Trait Implementation
Types implement traits using the @TraitName annotation:
trait Printable {
fn print;
}
type Point {
x i32
y i32
fn new($x i32, $y i32): Point {
return Point { x: $x, y: $y };
}
// Trait method defined with @TraitName annotation
@Printable
fn print {
echo "({}, {})", $this.x, $this.y;
}
}
Another Example
type Vector {
x f64
y f64
@Printable
fn print {
echo "({}, {})", $this.x, $this.y;
}
}
Generic Constraints
Use traits to constrain generic types:
fn print_all<T: Printable>($items &[T]) {
for $item in $items {
$item.print();
}
}
// Works with both classes and structs
$points = [Point::new(0, 0), Point::new(1, 1)];
print_all(&$points);
$vectors = [Vector { x: 1.0, y: 2.0 }, Vector { x: 3.0, y: 4.0 }];
print_all(&$vectors);
This compiles to efficient code for each concrete type implementing Printable through monomorphization — a separate, optimized version for each type.
The Result
You write high-level, expressive code. The compiler generates efficient machine code. No runtime overhead, no sacrifices.
- Generics: Zero-cost polymorphism
- Iterators: High-level composition with loop performance
- Inlining: Abstraction layers disappear
- Monomorphization: Generic code becomes concrete code
High-level and fast. That's the power of zero-cost abstractions.