Modules
Chapter 8 of 9Splitting a program across files.
Many files
Real programs outgrow one file. In Bert, every .bert file is a module, and its name comes from where it sits in your project.
The folder with bert.toml is the root. A file at Geometry/Shapes.bert is the module Geometry.Shapes.
A module decides what others can use with an export line at the top. Anything not listed stays private:
Exported functions need a type signature, and so does any type they mention. Checking the module on its own does not catch a missing one, but building any program that uses the module does, and the build stops until you add it. So anyone can see what a module offers by reading its first lines.
There are no import lines. To use another module, write its full name in front of what you want:
If the full name gets long, give it a short one at the top of the file. The short name goes above the export line, if the file has one:
Below the export line it is an error: Alias declarations must appear at the top of the file, above the first binding, type, or test. (The compiler calls a line of the form name = ... a binding.)
Because every use shows its module, you can tell where any function comes from just by reading it. List.map and String.concat are modules too. Bert ships them, so they are always there.
Using a private name from another module is an error: `triple` is private to `Geometry.Shapes`. Modules also can't depend on each other in a circle.
Types with an @
Sometimes a signature names a type with an @, like Bytes.@Bytes:
The @ marks a type that a module made for itself, and only that module can build one. You do not build it yourself: you use the module's functions, like Bytes.fromString here. You can skip making your own for now. The web server tutorial uses these and explains them there.
Next, the Tests chapter shows how to check that a module works, in the same file.