Modules

Imports

Every .crow file is its own module. All of the declarations in that file are in the scope of the module.
For another module to see a declaration, it must import it.

Named imports

# greetings.crow hello string() "hello, world"
import ./greetings: hello main void() info log hello

Whole-module imports

You can also omit the list of particular names to import to import all names from the module.

import ./greetings main void() info log hello

Module import paths

Relative imports

For importing your own code, the import syntax is designed to look like Unix-style relative paths (minus the .crow extension).
E.g., to import from ../foo/bar.crow, you would write ../foo/bar.

Library imports

An import like crow/io/print that starts with a plain name (as opposed to ./ or ../) is a library import.
The first part of the path is the library name. In crow/io/print, the library name is crow.
A global import first looks up the library path, then appends the rest of the path to that.
If the library name is crow or system, the library path is include/crow or include/system from where Crow is installed.
Otherwise, the library path must be specified in crow-config.json. (See config.)

File imports

Another interesting import in crow-demo/sdl/main.crow is ./crow.bmp as crow-bmp nat8 array.
This creates a constant crow-bmp of type nat8 array, containing the contents of the file.

import ./hello.txt as hello string main void() info log hello

An advantage of having the file contents available as a constant without having to read the file at runtime is that it can be used in functions that aren't summon.

Modules are linear

Dependencies can only work in one direction. Two modules can't import each other; only one may depend on the other.
If you have two modules that you need to depend on each other, the solution is usually to declare types in a common module that can be imported by both.
Specs, explained in Specs, can also help by replacing direct function dependencies.

Visibility

"Visibility" controls whether declarations (functions, types, etc.) can be imported.
If you don't care for this kind of feature and you aren't writing a library, you can ignore this section; the default visibility doesn't restrict anything within your own code.

The three kinds of visibility a declaration are:

Symbol Description
+ Public visibility; any module can import it.
~ Internal visibility; it can be imported by a relative import but not by a library import.
- Private visibility; it can't be imported. It's only visible within its module.

By default, declarations (like functions, types, etc.) have internal visibility.
This is the default because it prevents accidentally adding public functions, but doesn't add friction to calling functions you wrote.
If you aren't writing a library, you could choose to never specify visibility, and everything will be accessible within your code.

To make a declaration public, put a + in front of it.
To make it private, put a + in front of it.
Although it is the default, it can be made explicitly internal with a ~ in front of the name.

# dimensions.crow +dimensions record -width float -height float ~new dimensions(side float) side, side +area float(a dimensions) a.width * a.height -debug string(a dimensions) "{a.width}, {a.height}"
import ./dimensions main void() a dimensions = 4, info log "{a.area}" info log a.debug # above has a compile error, will throw

In this example, you can't call width, height, or debug from outside dimensions.crow.
You also can't call the default new function for dimensions that takes arguments for width and height, since those are private.
You can call the other new function which is declared internal. (Functions with the same name don't have to have the same visibility.)

A record itself can be public while its fields are private. This allows you to define abstract data types.