1. Modules, values, and functions
A Flecha file contributes declarations to the logical module named by its
module header. Files are not namespaces: two files can contribute to one
module, while @imports selects exported declarations from another module.
Declarations place the type before the name. Primitive families include signed
and unsigned integers, floating-point values, bool, char, and string views.
Use := when the initializer makes a local type obvious; use an explicit type
at public boundaries and where width matters. Functions state parameter and
return types, and export is an API decision rather than a default.
Read modules and imports beside the language
specification. In a scratch project, split one pure
calculation into src/score.fl and call it from src/main.fl; then use
longbow check to confirm module and type names without linking.
Tested example
Build one structure through its lifecycle method, then call behavior through an explicit receiver.
File: curriculum/main-evidence/single/structures.fl
// Build with: build/flecha build examples/structures.fl -o /tmp/structures// The program returns 42.structure Box { i32 x; private { i32 y; bool ready; }};
Box Box::init(i32 x, i32 y) override { return build Box { self.x = x; self.y = y; self.ready = true; };}
private i32 Box::hidden(&const self) { return self->y; }i32 Box::value(&const self) { return self->x + self->hidden(); }void Box::setY(&self, i32 value) { self->y = value; }unsafe i32 Box::rawValue(*const self) { return self->x + self->y; }
structure NamedBox { string name = "answer"; Box box = Box::init(2, 40); // Explicit arguments for this constructor.};
// An unsafe tear forbids copying and moving this type. The raw target below// remains alive until cleanup; unsafe code must uphold that requirement.structure Counter { *i32 total;};unsafe void Counter::tear(&self) override { if self->total != null { *self->total = *self->total + 1; }}unsafe Counter make_counter(*i32 total) { return build Counter { self.total = total; };}
i32 main() { NamedBox named = NamedBox::init(); NamedBox copy = named; // Independently owns a copy of the string field. named.name = "changed"; if copy.name != "answer" { return 1; }
copy.box.setY(39); { ref := ©.box; ref->setY(40); } unsafe { *Box ptr = &raw copy.box; if ptr->rawValue() != 42 { return 2; } }
i32 cleanups = 0; unsafe { Counter counter = make_counter(&raw cleanups); // Fresh factory return constructs directly in counter's storage. // tear runs automatically at this scope's end. } if cleanups != 1 { return 3; } return copy.box.value();}The block above is the complete file executed by the commands below.
The lifecycle method initializes both fields before the value is used. The member call then makes the receiver explicit, so construction and behavior remain attached to the declared type.
Commands
flecha check curriculum/main-evidence/single/structures.flflecha build curriculum/main-evidence/single/structures.fl -o build/structuresbuild/structuresExpected output
(empty stdout)exit status: 42Related reference: /examples/structures/.