Namespaces
Namespaces are collections of related definitions that make them modular and help them avoid name collisions. A common example of a namespace is a data type and associated operations over it, such as stacks or queues. A namespace can contain the declaration of a type that represents a currency with functions that manipulate the currency and constants that apply to it. Other pieces of code can access these definitions, providing maintainability, reusability and safety.
Developers often put a single smart contract in a namespace, but LIGO does not require you to put a contract in a namespace and it does not limit namespaces to contain a single contract.
Namespaces have some similarities with records because they can both contain multiple definitions. However, there are significant differences between records and namespaces:
Records are expressions and therefore can be used as values, and namespaces are not expressions and can't be used as values. For example, you can pass a record as an argument to a function, but you cannot pass a namespace in this way except in specific circumstances, such using the
contract_of
function to create a contract from a namespace to use in Testing.Records cannot package type and value definitions together like namespaces can.
Which construct you use depends on your design and strategy: namespaces behave like libraries and records behave like individual units of computation.
Creating namespaces
To create a namespace, declare it with the keyword namespace
and a name that starts with a capital letter.
Then put the type, function, value, and nested namespace definitions in a block opened by "{
" and closed by "}
"
For example, the following code defines a namespace named Euro
to represent the Euro currency.
It packages together a type (internally called t
), an operation add
that sums two values of the given currency, and constants for one and two Euros.
To access the contents of a namespace, use the name of the namespace and the selection operator ".
", as with records.
For example, this piece of code in the same file defines a value of the Euro
type and uses the functions and constants in the namespace to manipulate it:
In principle, you could change the internal implementation of the Euro
namespace without having to change the euro_balance
type or the add_tip
function.
For example, if you decide to support manipulating negative values, you could change the Euro.t
type to an integer:
The add_tip
function still works, and no change is needed.
Abstraction accomplished!
However, clients that use the Euro
namespace might still break the abstraction if they directly use the underlying representation of Euro.t
.
For example, the type Euro.t
is a transparent alias of nat
(or int
).
In this case, other code might break if it performs operations that are valid on nats but not on integers.
Client code should always try to respect the interface provided by the namespace and not make assumptions on its current underlying representation.
Nesting namespaces
You can define a namespace inside another namespace.
For example, this version of the Euro
namespace groups constants in a sub-namespace:
To access nested namespaces, use the selection operator as many times as necessary:
Note that the sub-namespace Coin
must be prefixed by the keyword export
to make its contents available outside the namespace.
Aliasing namespaces
You can apply an alias to a namespace to create namespaces that work as synonyms of previously defined namespaces.
Creating a synonym of a namespace can be useful to implement a namespace that is currently the same as another namespace for now but may need to change in the future.
For example, until 2025, the Bulgarian Lev is pegged to the euro currency, so the Bulgarian_Lev
namespace is an alias of the Euro
namespace:
Now other code can use the Lev just like it is a Euro for now, and you can change how the Lev works later.
You must use the import
keyword to alias the namespace, even if it is in the same file.
You can import namespaces from other files with the #import
directive, but only if the namespaces have the @public
decorator.
See #import
.