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
Whole-module imports
You can also omit the list of particular names to import to import all names from the module.
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.
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.
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.