Skip to content

The language

Modules

A module is a .br file. import geometry loads geometry.br from the main file’s directory, and import util.strings loads util/strings.br. You then write geometry.area(...), or bring names in directly with from geometry import area. There are no visibility modifiers: every top-level item in a module can be imported.

import geometry # geometry.br
import util.strings as text # util/strings.br, used as `text`
from geometry import Point, area # use these two names directly
fn main():
let q = geometry.Point(x=3.0, y=4.0)
print(Point(x=0.0, y=0.0).dist(q), area(2.0, 3.0), text.shout("hi"))

In a project made by brine new, the main file is src/main.br, so modules live under src/:

shapes/
├── brine.toml
├── AGENTS.md
├── README.md
└── src/
├── main.br
├── geometry.br
└── util/
└── strings.br

A module holds the same items as any file: functions, structs, enums, constants and tests. It has no main:

src/geometry.br
# Points, rectangles and areas.
const ORIGIN_X = 0.0
struct Point:
x: float
y: float
fn dist(self, other: Point) -> float:
let dx = self.x - other.x
let dy = self.y - other.y
return sqrt(dx * dx + dy * dy)
struct Rect:
w: float
h: float
fn area(self) -> float:
return area(self.w, self.h)
fn area(w: float, h: float) -> float:
return w * h
test "a 2 by 3 rectangle":
assert Rect(w=2.0, h=3.0).area() == 6.0
src/util/strings.br
# Small text helpers.
fn shout(s: str) -> str:
return s.upper() + "!"
fn pad_left(s: str, width: int) -> str:
if len(s) >= width:
return s
return " ".repeat(width - len(s)) + s
src/main.br
import geometry
import util.strings as text
from geometry import Point, area
fn main():
let p = Point(x=0.0, y=0.0)
let q = geometry.Point(x=3.0, y=4.0)
let r = geometry.Rect(w=2.0, h=1.5)
print(p.dist(q), area(2.0, 3.0), r.area(), geometry.ORIGIN_X)
print(text.shout("done"), "[" + text.pad_left("7", 3) + "]")
test "points are 5 apart":
assert Point(x=0.0, y=0.0).dist(Point(x=3.0, y=4.0)) == 5.0
Terminal window
brine run -q
Output
5.0 6.0 3.0 0.0
DONE! [ 7]
Terminal window
brine test
Output
Built tests [dev] in 0.09s
test points are 5 apart ... ok
test geometry: a 2 by 3 rectangle ... ok
test result: ok. 2 passed; 0 failed; finished in 0.02s

brine test runs the tests in the main file and in every module it imports. Tests from a module are named with the module first (geometry: ...).

Outside a project, brine run app.br works the same way: modules are found next to app.br.

import geometry # use as geometry.area(...), geometry.Point(...)
import util.strings # use as strings.shout(...)
import util.strings as s # use as s.shout(...)
import geometry as g # an alias works for any module
  • import a.b.c loads a/b/c.br and binds the last part, c. Add as name to pick another name.
  • Everything in the module is reached through the name: functions (geometry.area(1.0, 2.0)), constructors (geometry.Point(x=1.0, y=2.0)), types in annotations (let ps: [geometry.Point] = []), static methods, enum variants (geometry.Shape.Circle(1.0), also in match patterns) and constants (geometry.ORIGIN_X).
  • Paths are always relative to the main file’s directory, also in imports written inside other modules: in src/util/strings.br, import geometry loads src/geometry.br.
  • Modules may import each other, including in a cycle.

from m import a, b makes items of m usable without the m. prefix:

from geometry import Point, Rect, area, ORIGIN_X
import geometry # you can import the module too
fn main():
let p = Point(x=1.0, y=2.0)
print(area(2.0, 3.0), geometry.area(1.0, 1.0), p, ORIGIN_X)

Two rules keep names unambiguous:

  1. from m import x only imports items defined in m itself. If m imported x from somewhere else, import it from there.
  2. A name can’t be both imported and defined in the same module.
from geometry import Point, area
fn area(r: Point) -> float:
return r.x * r.y
fn main():
print(area(Point(x=2.0, y=3.0)))
error[E0202]: `area` is imported but this module already has that name
--> src/main.br:1:29
|
1 | from geometry import Point, area
| ^^^^ imported here
3 | fn area(r: Point) -> float:
| ---- also defined here
|
= help: rename one of them, or use `import geometry` and write `geometry.area`
= run `brine explain E0202` for more about this error
error: could not compile due to 1 error

A misspelled name is E0201, with a suggestion:

error[E0201]: module `geometry` has no item `Pointt`
--> src/main.br:1:22
|
1 | from geometry import Pointt
| ^^^^^^ not found
|
= help: did you mean `Point`?
= run `brine explain E0201` for more about this error
error: could not compile due to 1 error
import geometry
import shape
fn main():
print(geometry.area(1.0, 2.0))
error[E0701]: module `shape` not found
--> src/main.br:2:1
|
2 | import shape
| ^^^^^^^^^^^^ expected a file at `src/shape.br`
|
= help: modules are `.br` files next to the main file; `import a.b` loads `a/b.br`
= run `brine explain E0701` for more about this error
error: could not compile due to 1 error

brine check src/geometry.br type-checks and proves a module on its own, and brine test src/geometry.br runs its tests. brine run needs a file with fn main (error E0703 otherwise). brine fmt formats every .br file in the project.

  • One file is one module. import a.b loads a/b.br relative to the main file’s directory and binds b (or the as name).
  • Every top-level item is importable; there is no pub or private.
  • from m import x imports only items defined in m.
  • A name can’t be both imported and defined in one module.
  • Each module’s tests run with brine test once the main file imports it.

Related: structs, testing, the CLI, functions.