Overview
A namespace is a collection of names associated with values and functions. Namespaces limit the scope of visibility and help to structure the code. MUFL namespaces behave similarly to C++ namespaces.
When you declare a library, an application, or a script, a namespace is declared implicitly using the name of the library, application, or script. You can also use the module
keyword to declare additional, nested namespaces.
In cases where names can be used outside of the namespace in which they were declared, the access must be made using name resolution rules, that is, they must either use the using
statement to introduce the external names, or refer to them using multipart names with the ::
separator.
The using <namespace_name>
syntax allows accessing names within the given module without explicitly specifying the name of the module every time. However, referring to an ambiguous name (for example, when the same name is present in multiple namespaces referenced with the using
statement) results in a compile-time error.
script a {
x = "a--x".
module b {
x = "b--x".
y = "b--y".
_print x "\n".
_print a::x "\n".
_print ::a::b::x "\n".
}
_print b::x "\n".
using b.
// _print x "\n". // error -- this name is now ambiguous
_print y "\n".
}
Prints:
b--x
a--x
b--x
b--x
b--y
Visibility and Loading
Modules and libraries expose their functions, variables, user-defined types, and nested modules. They can be accessed by the name of the namespace, which always corresponds to the name of the module or library.
library A
{
var = "hello ".
fn printing_function (_)
{
_print "world\n".
}
}
application B loads library A
{
_print A::var.
A::printing_function().
}
Prints:
hello world
MUFL precludes implicit visibility between libraries. That is, if library A loads library B, and library B loads library C, then code inside A does not have transitive access to the names inside C. In order to gain visibility, A must load
C explicitly.
library A
{
var = "hello".
}
library B loads library A
{
var = A::var. // this works as expected
}
script X loads library B
{
_print B::var. // this works fine
_print A::var. // this throws an error since `A` is not accessible here
}
Results in this expected compiler output:
---COMPILER OUTPUT----
<...>Symbol A::var was not found
Compilation Error
ERROR running compiler
Hidden Directive
A hidden
code block can be used to prevent outside access to some of the names inside a namespace. The names are still accessible by the code inside the namespace and within its nested namespaces.
library A
{
hidden
{
var = "hello ".
}
fn print_hidden (_)
{
_print var "\n".
}
}
library B loads library A
{
A::print_hidden(). // this one works because the function accesses its own data
_print A::var. // leads to a compilation error
}
Results in this expected error:
---COMPILER OUTPUT----
<...>Symbol var is hidden
Compilation Error
ERROR running compiler
Name Resolution Rules
Simple Names
The MUFL compiler resolves simple (local) names through a layer-by-layer examination of namespaces, from innermost to outermost. Specifically:
- If a simple name maps to multiple locations, a compilation error ensues. For example, when the
using
keyword references a nested namespace housing an identical simple name. - In cases where a namespace does not include a simple name, the search progresses to the parent namespace.
Absolute Path Names
Names identified by their absolute path are resolved by investigating namespaces from the root to innermost layers, section by section. Notably:
- If a name corresponds to multiple locations within a namespace section, it triggers a compilation error.
- The absence of a section within a namespace also results in a compilation error.