Modules
A module is a collection of source code, such as types and methods, in its own namespace. Each Inko source file is automatically a module. There is no way to define a module manually.
Module names
The name of a module is derived from the path (relative to a source directory) of the Inko source file the module belongs to. Imagine your project structure is as follows:
src/
└── foo/
└── bar.inko
Here src/
is added to the list of directories the compiler will search for
Inko source files. The foo/
directory defines a namespace (foo
), and
contains a single module: bar
. In Inko, the namespace separator is ::
,
meaning the full name of this module is foo::bar
.
The methods and types in a module are always public, and there is no way to
declare these as private. If your module relies on certain types or methods that
you don't want to expose as part of the public API, moving these types and/or
methods to a separate module is the best solution. For example, the module
std::fs::file
relies on various methods provided by the module
std::fs::bits
.
Info
We are considering adding support for private methods and constants, though this is not a priority for the time being. For more information, take a look at the issue "Private constants and methods".
Referring to the current module
Inside a module, self
refers to the module itself:
def foo {}
self.foo # Same as just `foo`
You can also use the global ThisModule
, which is automatically defined for
every module and refers to the module itself:
def foo {}
ThisModule.foo # Same as `self.foo`, which is the same as `foo` in this case
This is useful if we want to send a message to the module, but the lack of an explicit receiver would cause a conflict:
def example -> Integer {
10
}
class Example {
def example -> Integer {
# This ensures we return `10`, instead of recursing back into the current
# method.
ThisModule.example
}
}
Importing modules
Modules can import other modules, as well as their types; optionally binding
them using a different name. You can import a module using the import
keyword.
Imports can only occur at the top-level of a module. So this is fine:
import foo
But this is not:
def example {
import foo
}
Importing a module itself is done as follows:
import std::fs::file
The file
module can then be accessed using the file
global.
Importing a single constant from a module (instead of importing the module as a whole) is done as follows:
import std::fs::file::ReadOnlyFile
If you want to import multiple constants, you can do so as follows:
import std::fs::file::(ReadOnlyFile, WriteOnlyFile)
If you want to bind a constant to a different name, you can do so as follows:
import std::fs::file::(ReadOnlyFile as File)
File # => OK
ReadOnlyFile # => undefined
If you want to import both a module and some of its constants, you can do so as follows:
import std::fs::file::(self, ReadOnlyFile)
You can also import a module as a whole and bind it to a different name:
import std::fs::file::(self as foo)
Info
Importing of methods isn't supported. For more information, refer to the issue "Allow importing of just methods from a module".
Import order
Imports are always processed before executing any code in a module, in the same
order as the import
statements. This means that this:
import std::stdio::stdout
stdout.print('hello')
import std::fs::file
Is executed as if it were written like this:
import std::stdio::stdout
import std::fs::file
stdout.print('hello')