Skip to content

Local packages and imports

Most of your own code lives in local packages under your module root. The rules are small and consistent. Resolution is done in compiler/beans/module.b.

A directory is a package. Every .b file in that directory declares the same package clause, written in snake_case:

package money

Files of the same package do not import each other. They already share one namespace. You only import to reach a different package.

Sub-packages are subdirectories under the module root. A directory shop/money/ becomes the import path shop.money:

import shop.money
fn main() {
let m: Money = money.zero()
}

Note two things:

  • You use the package by its declared package name (money), not the last path segment. Usually they match, but they do not have to.

  • You can rename the binding for one file with as:

    import shop.money as cash

When you write import X, Beans decides what X is by its shape:

Shape of XResolves to
std.*The shipped standard library.
<module_name> or <module_name>.<...>A local package under your module root.
host/owner/repo[/sub...] (first segment has a ., three or more segments)A Git dependency cloned to the cache.
anything elseAn error.

When an import matches none of these shapes, Beans reports it as an unknown package and lists what it expected: std.*, <module>.*, or a git host path.

A package’s identity is its full import path, not its declared name. Two packages may call themselves the same name as long as their import paths differ. For example shop.a.cart and shop.b.cart can both use package cart, and they stay distinct packages.

Import cycles are refused. When packages import each other in a loop, Beans prints the full chain of import sites so you can see the cycle.

From another package you can reach anything marked pub on the imported package: functions and types. To use new on a class, both the class and its init must be pub. A plain fn init remains usable throughout its own package.

import shop.util
fn main() {
util.some_fn()
let u: util.User = new util.User("ada")
let name: string = u.name
}

If there is no beans.pot above a lone file, you are in single-file mode. You can still import std.* and Git dependencies, but you cannot use local packages, because there is no module root to hang them off of.

The imports guide covers imports in everyday code, and a local-package project works through a full example.