Modules and imports
This section provides examples of different scenarios for using the
To illustrate how these keywords are used, let’s step through some sample code.
One of the most common import scenarios is one that you see illustrated in the examples in this guide, in the Motoko projects in the examples repository, and in the tutorials involves importing modules from the Motoko base library. Importing modules from the base library enables you to re-use the values, functions and types defined in those modules rather than writing similar ones from scratch.
The following two lines import functions from the
import Array "mo:base/Array"; import Result "mo:base/Result";
Notice that the import declaration includes the
mo: prefix to identify the module as a Motoko module and that the declaration does not include the
.mo file type extension.
Another common approach to writing programs in Motoko involves splitting up the source code into different modules. For example, you might design an application to use the following model:
main.mofile to contain the actor and functions that change state.
types.mofile for all of your custom type definitions.
utils.mofile for functions that do work outside of the actor.
In this scenario, you might place all three files in the same directory and use a local import to make the functions available where they are needed.
For example, the
main.mo contains the following lines to reference the modules in the same directory:
import Types "types"; import Utils "utils";
Because these lines import modules from the local project instead of the Motoko library, these import declarations don’t use the
In this example, both the
utils.mo files are in the same directory as the
Once again, import does not use the `.mo` file suffix.
You can also import modules from other packages or from directories other than the local directory.
For example, the following lines import modules from a
redraw package that is defined as a dependency:
import Render "mo:redraw/Render"; import Mono5x5 "mo:redraw/glyph/Mono5x5";
You can define dependencies for a project using the Vessel package manager or in the project
dfx.json configuration file.
In this example, the
Render module is in the default location for source code in the
redraw package and the
Mono5x5 module is in a
redraw package subdirectory called
In addition to the examples above that import Motoko modules, you can also import functions from canisters by using the
canister: prefix in place of the
For example, you might have a project that produces the following three canisters:
These three canisters are declared in the project’s
dfx.json configuration file and compiled by running
You can then use the following lines to import the functions from the
Connectd canisters in the LinkedUp program:
import BigMap "canister:BigMap"; import Connectd "canister:connectd";
When importing canisters, it is important to note that the type for the imported canister corresponds to a Motoko actor instead of a Motoko module. This distinction can affect how some data structures are typed.
For the imported canister actor, types are derived from the Candid file—the project-name.did file—for the canister rather than from Motoko itself.
The translation from Motoko actor type to Candid service type is mostly, but not entirely, one-to-one, and there are some distinct Motoko types that map to the same Candid type. For example, the Motoko
Word8 types both exported as Candid type
nat8 is canonically imported as Motoko
The type of an imported canister function, therefore, might differ from the type of the original Motoko code that implements it.
For example, if the Motoko function had type
+shared Word8 → async Word64 in the implementation, its exported Candid type would be
(Nat8) -> Nat64 but the Motoko type imported from this Candid type will actually be the correct—but perhaps unexpected—type
shared Nat8 -> async Nat64.
These type differences are the result of the Candid-to-Motoko composition layer inherent to the canister abstraction.
Although the most common convention is to identify imported modules by the module name as illustrated in the examples above, there’s no requirement for you to do so. For example, you might want to use different names to avoid naming conflicts or to simplify the naming scheme.
The following examples illustrate different names you might use when importing the
List base library module, avoiding a clash with another
List library from a fictional
import List "mo:base/List:"; import Sequence "mo:collections/List"; import L "mo:base/List";