Skip to content

2. Collections, allocation errors, and generics

Dynamic collections own allocations and can fail while growing. Reserve when you know a useful capacity, but still preserve the error result: lack of memory is not a value. Views borrow collection storage and therefore constrain moves, growth, replacement, and destruction until their final use.

Generics specialize checked code for concrete types. Type sets express the operations a parameter supports; callables carry behavior across an API. Start with a concrete implementation, make the varying type or operation explicit, and inspect diagnostics at the specialization site. Avoid abstraction that merely hides ownership or allocation.

Use collections, runtime arrays and containers, generics, and callables as focused companions.

Tested example

Compose typed callables, a generic relay, and captured values without erasing their contracts.

File: curriculum/main-evidence/single/callables.fl

curriculum/main-evidence/single/callables.fl
func Mapper(i32) => (i32);
func GenericMapper<T>(T) => (T);
structure Pipeline { Mapper callback; };
Pipeline Pipeline::init(Mapper callback) <- callback override {
return build Pipeline { self.callback = callback; };
}
i32 Pipeline::run(&const self, i32 value) {
return (self->callback)(value);
}
T relay<T>(T value) <- value { return value; }
Mapper offset(i32 amount) {
return [copy amount](value) => { return value + amount; };
}
Mapper combine([2]Mapper callbacks) <- callbacks {
return [own callbacks](value) => {
return (callbacks[0])(value) + (callbacks[1])(0);
};
}
i32 main() {
Mapper explicit = [](i32 value) => (i32) { return value * 2; };
Mapper inferred = (value) => { return value + 2; };
Pipeline pipeline = Pipeline::init(offset(20));
Mapper generic = relay<Mapper>(explicit);
GenericMapper<i32> specialized = (value) => { return value + 1; };
if generic(21) != 42 { return 1; }
if inferred(40) != 42 { return 2; }
if specialized(41) != 42 { return 4; }
[2]Mapper pending = [offset(10), offset(12)];
Mapper nested = combine(pending);
if nested(20) != 42 { return 3; }
return pipeline.run(22);
}

The block above is the complete file executed by the commands below.

The callable signatures preserve their parameter and result types through composition. The generic relay does not erase those contracts, so incompatible functions still fail during checking.

Commands

Commands
flecha check curriculum/main-evidence/single/callables.fl
flecha build curriculum/main-evidence/single/callables.fl -o build/callables
build/callables

Expected output

Expected output
(empty stdout)
exit status: 42

Related reference: /examples/callables/.