The previous import page indicated that external libraries are imported as packages. In addition, any program or library you are building is also a package. A package, not a source file, is the basic unit of compilation.
A package is a collection of one or more source files that are transformed by the Congo build system into a specific program or library. This page describes how to name and organize the source files for your program or library across folders, as well as the language features used to bind them together at build time. It also describes how a package's source code can be organized into module namespaces. By convention, the names and structure for packages and modules typically map closely to how source files are named and organized into folders.
A Single Source File
Each package typically lives in its own base folder. So, for a new program, you begin by creating that folder. Within it, 'congo new' creates the necessary scaffolding for a new program. In the folder, you will find it created a new 'main.cone' file. This name is what Congo starts with when compiling your package.
You can use your favorite editor to modify main.cone, transforming it into the program you want to create. To compile it, you only need to invoke 'congo build' within that folder.
If you want to build a library rather than an executable program, use 'congo new library' instead. The main source file for a library is called 'lib.cone'.
When your package expands to more than one source file, you have two options for binding the source files together:
- include, which stitches one source file into another as part of the same namespace.
- mod, which creates a separate module namespace.
The include statement incorporates another source file's contents as if were made a part of the current source file. For example, suppose we had a source file called 'useme.cone':
fn inc(a i32) i32 a+1
Another source file can now use its contents:
include useme fn timesnext(a i32) a * inc(a) // uses a function defined by 'useme.cone'
'useme' is the name of the source file to include (the extension of .cone is assumed, if not specified). By default, it looks for this file in the same folder as the source file which includes it.
Sometimes, it is conceptually useful to organize include files in a nested structure, mirrored by the structure of the folders that hold the source files. The build system offers a simple convention for accomplishing this.
In the above example, if Congo failed to find 'useme.cone' in the same folder, it will look inside the 'useme' subfolder for a file named 'mod.cone'.
Quoted include files
Sometimes, a source file's name or path may need to use punctuation characters not legal in an identifier, such as '/' or '.'. When this is the case, simply enclose the file/path name in double quotes:
A specified path may be absolute or relative to the current source file's folder.
The alternative to include is mod, which marks code as belonging to a distinct module. A module is a global namespace for named types, functions, global variables, metaprogramming constructs, and other modules. A module may lie within a source file, be a whole source file, or even span multiple source files. Modules are nestable.
Modules are valuable for isolating the names used by large programs. Modules help to avoid situations where different source programs accidentally collide because they use the same name to refer to different things.
Global variables are declared outside of all function blocks. Space for them is allocated automatically when a program is loaded for execution. Global variables are shared across and accessible by any function at any time.
imm glowy i32 // Global immutable variable fn a_function() mut loco f32 // Local mutable variable
The lifetime of global variables is the same as the program's lifetime. Global variables come into being when the program starts and disappear when the program stops.
Modules as separate files
Typically, a nested module is defined in its own source file and then referenced by any module that needs access to its named members. Using mod, a program can include 'useme.cone' as a module (instead of an include file):
mod useme fn timesnext(a i32) a * useme::inc(a) // uses a function defined by 'useme.cone'
We need to qualify the inc() function with the module's namespace name to access it, exactly as we needed to when we import foreign packages (which are also treated as module namespaces). Indeed, all the advice given there about namespace-qualified names applies here as well. Since modules can be nested inside modules, namespace-qualified names can get deep (e.g., ip::tcp::http).
Furthermore, mod supports the same options as import for folding (and aliasing) specific module names into the current namespace. Like include, mod also supports automatic folder nesting (e.g., "useme/mod.cone") and quoted filename paths.
Although include useme and mod useme * might appear to do the same thing, there are some subtly important differences:
- Every use of include retrieves and parses the file, whereas mod will only do this once for any given source file.
- Using mod, the source file's private names (those beginning with an underscore) will not become part of the current namespace.
- Processing mod is somewhat slower than include.
It is possible to specify a separate module within a source file using the mod statement followed by a block. Its named namespace is effectively nested within the enclosing program's namespace:
mod frontend fn get() ... fn main() frontend::get() // refers to the get function within the frontend namespace