Skip to main content

Modules and packages

Directory as a module

A module in Fuyu is defined by a directory. All *.fuyu files that reside in the same directory belong to that module, and the declarations made in any of those files are shared across the entire module.

  • Module naming: The base module takes the name of the package to which it belongs (e.g., my_package) and the submodules are named by concatenating the package name with the subdirectory name (e.g., my_package/util/math_helpers).
  • Shared scope: Every declaration in any *.fuyu file within the directory is visible to all other files in the same directory.
  • Visibility: All declarations are private to their module by default. Private items are freely usable within that module, while only public symbols can be accessed by other modules in the same package.
  • Test files: Files matching *.test.fuyu are treated as test sources, and are excluded from non-test builds.

The purpose of these rules is to:

  1. Eliminate the need to define the package structure.
  2. Make the package structure match the file system.

Packages

Pending

Packages are part of ongoing design work.

Imports

info

Import declarations are only allowed at module level.

Imports are scoped to a file, not the entire module.

An import pulls in the module, giving it a namespace to access its contents.

// - Creates `vehicle` namespace.
import model/vehicle

Use a for clause to pull in specific items.


// - Creates `vehicle` namespace.
// - Imports trait `Vehicle`.
// - Imports type `Car`.
// - Imports constructors `Sedan`, `Truck`, and `Van`.
// - Imports function `drive`.
import model/vehicle for trait Vehicle, type Car, Sedan, Truck, Van, drive

Use as to rename.

// - Creates `automobile` namespace (`vehicle` namespace is not created).
// - Imports type `Car` as `Automobile` (`Car` is not defined).
// - Imports constructor `Van` as `PeopleCarrier` (`Van` is not defined).
// - Imports function `drive` as `go` (`drive` is not defined).
// - All public declarations in `model/vehicle` are available through the
// `automobile` namespace under their original names (e.g., `automobile.Van`
// but not `automobile.PeopleCarrier`).
import model/vehicle as automobile for
trait Vehicle as Craft,
type Car as Automobile,
Van as PeopleCarrier,
drive as go

The namesapce can be hidden if it is really not needed.

// Does not create any namespaces.
// - Imports type `Car`.
import model/vehicle as _ for type Car

Acyclic dependencies

When a module imports another module, it creates a dependency relationship. Connecting all of these relationships for a program yields a dependency graph. The dependency graph must be acyclic, meaning that no module (or package) may ultimately depend on itself, either directly or indirectly. In other words, there must be no cycles where two or more modules/packages depend on each other.

Because packages are collections of modules, the same rule applies at the package level. Both modules and packages must form acyclic dependency graphs.

Module search paths

Depending on the path given to import a module, different locations are searched.

There are three flavors of imports:

  1. Package path imports.
    import a/b/c // Search for `a/b/c` in the package path.
    This is how the standard library and external modules are imported.
  2. Relative path imports.
    import ./a/b/c     // Search for `a/b/c` relative to the current module.
    import ../a/b/c // Search for `a/b/c` relative to the parent module.
    import ../../a/b/c // Search for `a/b/c` relative to the grandparent module.
    This is one way that modules within the same package are imported.
  3. Absolute path imports.
    import /a/b/c // Search for `a/b/c` relative to the package root.
    This is one way that modules within the same package are imported.
tip

If an import path starts with an identifier (e.g., std), then it is importing a module outside of the current package.

If an import path starts with punctuation (e.g., /, ./, or ../), then it is importing a module inside of the current package.

Visibility

The @[public] attribute describes whether a declaration is visible when a module is imported. The @[transparent] attribute augments public types by stating that the internal structure of the type is also public.

@[public]
type Ab // Visible.
case A // Not visible.
case B // Not visible.

@[public]
@[transparent]
type Cd // Visible.
case C // Visible.
case D // Visible.

Transparency is all or none. Either all of the type details are made transparent or none of them. Types that are not transparent are called opaque, since no internal details are known about them, and they cannot be constructed or destructured outside of the context in which they were declared.

The same rules apply to traits. A trait that is not transparent can be used as a trait bound, however, it cannot be implemented and its functions cannot be used outside of the module that declares it. If transparent, it can be implemented and its functions can be used anywhere.