Bringing Paths into Scope with the use
Keyword
Having to write out the paths to access items can feel inconvenient and repetitive. In the previous
chapter, whether we chose the absolute or relative path to Asparagus
, every time we wanted to use
Asparagus
we had to specify the modules garden
and vegetables
. Fortunately, there’s a way to
simplify this process: we can create a shortcut to a path with the use
keyword once, and then use
the shorter name everywhere else in the scope.
In the example below, we bring the ::garden::vegetables
module into the scope of the root file to
use the Asparagus
enum:
use ::garden::vegetables;
predicate Baz(
third_asparagus: vegetables::Asparagus,
) { }
Adding use
and a path in a scope is similar to creating a symbolic link in the filesystem. By
adding use ::garden::vegetables
in the root file, vegetables
is now a valid name in that scope.
Handling conflicting imports
Pint does not allow bringing two items with the same name into scope with use
. This name clash
makes it impossible for the compiler to distinguish between the two items in the scope. There are
two ways to avoid this problem. The first, is to only import the names of the parent modules.
Consider the following two libraries:
// Module data::contract_lib
type Data = {
address: b256,
storage_vars: int,
predicates: int,
};
// Module data::predicate_lib
type Data = {
address: b256,
parameters: int,
};
Both libraries use the name Data
to describe a types. The example below shows how to bring the two
Data
types into scope and how to refer to them without having a conflict.
use ::data::contract_lib;
use ::data::predicate_lib;
predicate test(
contract_data: contract_lib::Data,
predicate_data: predicate_lib::Data,
) {}
As you can see, using the parent module distinguishes the two Data
types. If instead we specified
use data::contract_lib::Data
and use data::predicate_lib::Data
, we'd have two Data
types in
the same scope and Pint wouldn't know which one we meant when we used Data
.
There’s another solution to the problem of bringing two types of the same name into the same scope
with use
: after the path, we can specify as
and a new local name, or alias, for the type. The
example below shows another way to write the code in the previous example by renaming the two Data
types using as
.
use ::data::contract_lib::Data as ContractData;
use ::data::predicate_lib::Data as PredicateData;
predicate test(
contract_data: ContractData,
predicate_data: PredicateData,
) { }
In each use
statement, we choose a new name for Data
. That guarantees that no conflicts arise.
This also has the side benefit of giving Data
a more meaningful name in the current context.
Using Nested Paths to Clean Up Large use
Lists
If we’re using multiple items defined in the same module, listing each item on its own line can take
up a lot of vertical space in our files. For example, these two use
statements we had in the
previous example bring two items into scope:
use ::data::contract_lib::Data as ContractData;
use ::data::predicate_lib::Data as PredicateData;
Instead, we can use nested paths to bring the same items into scope in one line. We do this by specifying the common part of the path, followed by two colons, and then curly brackets around a list of the parts of the paths that differ, as shown below.
use ::data::{
contract_lib::Data as ContractData,
predicate_lib::Data as PredicateData
};
In bigger programs, bringing many items into scope from the same module using nested paths can
reduce the number of separate use
statements needed by a lot!
We can use a nested path at any level in a path, which is useful when combining two use
statements
that share a subpath. For example, the code snippet below shows two use
statements: one that
brings data::contract_lib
into scope and one that brings data::contract_lib::Data
into scope.
use ::data::contract_lib;
use ::data::contract_lib::Data;
The common part of these two paths is data::contract_lib
, and that’s the complete first path. To
merge these two paths into one use
statement, we can use self
in the nested path, as shown
below.
use ::data::contract_lib::{self, Data};
This line brings data::contract_lib
and data::contract_lib::Data
into scope.