.. default-domain:: chpl .. _primers-modules: Modules ======= `View modules.chpl on GitHub `_ This primer introduces the concept of modules, a concept for encapsulating code for use by other code. It covers: * how to define a module * namespace control within a module * access to another module's symbols * namespace control when using a module, including: * unlimited * explicit exclusion of symbols * explicit inclusion of symbols * renaming included symbols * cross-file module access A module is a grouping of code - each program consists of at least one module, and every symbol is associated with some module. If all the code of a file is not enclosed in an explicit module, defined using the ``module`` keyword, then the file itself is treated as a module with the same name as the file (minus the .chpl suffix). The compiler can be directed to include modules in distinct files by naming them on the command line or by relying on the ``-M`` flag (see the :ref:`man page ` for exact details). Here, we declare a module `ModToUse`: .. code-block:: chapel module ModToUse { In this case, ``foo`` is a public module-level variable that is defined within the module ``ModToUse`` .. code-block:: chapel var foo = 12; As is ``bar``. .. code-block:: chapel var bar: int = 2; A symbol can be declared ``private`` - this means that only code defined within the same scope as the definition of the symbol (including code in nested scopes) can access it. Here, ``hiddenFoo`` is a private module-level variable, making it only accessible to other code contained in ``ModToUse`` .. code-block:: chapel private var hiddenFoo = false; ``baz`` is a public function which is defined within ``ModToUse`` .. code-block:: chapel proc baz (x, y) { return x * (x + y); } ``hiddenBaz`` is a private function, which is also only accessible by symbols contained in ``ModToUse``. .. code-block:: chapel private proc hiddenBaz(a) { writeln(a); return a + 3; } ``Rec`` is a module-level record, with a field and a method defined inside it. .. code-block:: chapel record Rec { var field: int; proc method1 () { writeln("In Rec.method1()"); } } ``method2`` is a secondary method defined on ``Rec``. .. code-block:: chapel proc Rec.method2() { writeln("In Rec.method2()"); } } // end of ModToUse module In the current implementation, ``private`` cannot be applied to type definitions; type aliases, and declarations of enums, records, and classes cannot be declared private. Private also cannot be applied to fields or methods yet. Here are some other modules which will be used in the remainder of this file .. code-block:: chapel module AnotherModule { var a = false; } module ThirdModule { var b = -13.0; } module Conflict { This variable shares a name with a symbol in ``ModToUse``. .. code-block:: chapel var bar = 5; var other = 5.0 + 3i; var another = false; } // end of Conflict module module DifferentArguments { This function shares a name with a function in ``ModToUse``, but takes different arguments .. code-block:: chapel proc baz(x) { return x - 2; } } // end of DifferentArguments module module RecMoreMethods { private use ModToUse; ``method3`` is a tertiary method defined on ``Rec`` .. code-block:: chapel proc Rec.method3() { writeln("In Rec.method3()"); } } // end of RecMoreMethods module module MainModule { proc main() { writeln("Access from outside a module"); Access From Outside a Module ---------------------------- In multi-module programs, it is common for modules to access the contents of other modules. The starting point for doing so is the `use statement` or the `import statement` in Chapel. These statements can be inserted at any lexical scope that contains executable code. By default, a ``use`` statement makes all of a specific module's visible symbols available to the scope that contains that ``use`` statement. These symbols may then be accessed directly in an unqualified manner (without a module name prefix). In this case, ``bazBarFoo`` should store the result of calling ``ModToUse.baz`` on ``ModToUse.bar`` and ``ModToUse.foo``, which is in this case ``28``. .. code-block:: chapel { use ModToUse; var bazBarFoo = baz(bar, foo); writeln(bazBarFoo); } Since ``use`` statements only affect their containing scope, when we leave a scope like this, we lose access to the module's symbols. For instance, since the following line isn't within a scope that contains a ``use`` of ``ModToUse``, it would generate an error if uncommented. This is because ``foo`` is not visible within our lexical scope or via any ``use`` statements in that scope. .. code-block:: chapel // var twiceFoo = 2 * foo; An ``import`` statement, in contrast to a ``use`` statement, either enables qualified access to the visible symbols of a module (e.g. with the module's name as a prefix) or enables unqualified access to a specified subset of those symbols (e.g. without the module's name as a prefix). This demonstrates an ``import`` that enables access with the module prefix. In this example, ``bazBarFoo`` should store the result of calling ``ModToUse.baz`` on ``ModToUse.bar`` and ``ModToUse.foo``, which is in this case ``28``. .. code-block:: chapel { import ModToUse; var bazBarFoo = ModToUse.baz(ModToUse.bar, ModToUse.foo); writeln(bazBarFoo); } This case demonstrates an ``import`` that enables access without the module prefix to a single symbol within ``ModToUse``. .. code-block:: chapel { import ModToUse.bar; writeln(bar); } ``import`` statements also only affect their containing scope, so similarly the following line would generate an error if uncommented, due to being outside a scope with an ``import`` or ``use`` of ``ModToUse``. .. code-block:: chapel // var twiceFoo = 2 * ModToUse.foo; ``use`` statements apply to the entire scope in which they are defined. Even if the ``use`` statement occurs after code which would directly refer to its symbols, these accesses are still valid. This is similar to other Chapel forms of introducing symbols - for instance, function declaration order does not prevent a function declared earlier in a scope from calling one declared later. Thus, as in an earlier example, the following declaration of ``bazBarFoo`` will store the result of calling ``ModToUse.baz`` on ``ModToUse.bar`` and ``ModToUse.foo``, which is again ``28``. .. code-block:: chapel { var bazBarFoo = baz(bar, foo); use ModToUse; writeln(bazBarFoo); } Similarly, ``import`` statements also apply to the entire scope in which they are defined. Even if the ``import`` statement occurs after code which would directly refer to its symbols, these accesses are still valid. Thus, the output of this block is the same as that of its ``use`` statement counterpart. .. code-block:: chapel { var bazBarFoo = ModToUse.baz(ModToUse.bar, ModToUse.foo); import ModToUse; writeln(bazBarFoo); } Modules that are being used can be given a different name than the ones they were declared with. In this example, ``ModToUse.bar`` can be accessed using the new name, ``other``, as the module prefix. The old name is not visible in that scope, though, so writing just ``ModToUse.bar`` will not work. However, unprefixed access to the module's symbols remains unchanged. .. code-block:: chapel { use ModToUse as other; writeln(other.bar); // writeln(ModToUse.bar); // would be an error, ModToUse not visible writeln(bar); } The same is also true of modules that are imported. .. code-block:: chapel { import ModToUse as other; writeln(other.bar); // writeln(ModToUse.bar); // would be an error, ModToUse not visible } With this syntax, a ``use`` statement can enable just unqualified access to a symbol, similar to how the unqualified list syntax for ``import`` statements doesn't enable qualified access. .. code-block:: chapel { use ModToUse as _; writeln(bar); // writeln(ModToUse.bar); // would be an error, ModToUse not visible // writeln(_.bar); // also an error, _ is not an identifier otherwise } Variables provided by a private ``use`` statement are only considered when the name in question cannot be resolved directly within the local scope. Thus, because another ``bar`` is defined within this scope, the access to ``bar`` within the ``writeln`` will refer to the local variable ``bar`` rather than to ``ModToUse.bar``. .. code-block:: chapel { var bar = 4.0; use ModToUse; writeln(bar); // Will output the value of the bar defined in this scope (which is // '4.0'), rather than the value of ModToUse.bar (which is '2') } In contrast, defining a variable with the same name as something imported or brought in by ``public use`` leads to a multiple definition error. .. code-block:: chapel { import ModToUse.bar; // var bar = 4.0; multiple definition error } If a symbol cannot be resolved directly within the local scope, then the symbols provided by a ``private use`` / ```use`` statements are considered before the symbols defined outside of the scope where the ``use`` statement applies. Thus, because the other ``bar`` was defined outside of these curly braces, the compiler will find the ``bar`` from ``ModToUse`` when resolving the access within the ``writeln``, rather than the outer ``bar``. The ``bar`` from ``ModToUse`` is said to be "shadowing" the definition at the outer scope. .. code-block:: chapel { var bar = false; { use ModToUse; writeln(bar); // Will output the value of ModToUse.bar (which is '2'), rather // than the value of the bar defined outside of this scope (which // is 'false') } } The same is also true of symbols provided by an ``import`` statement. .. code-block:: chapel { var bar = false; { import ModToUse.bar; writeln(bar); // Will output the value of ModToUse.bar (which is '2'), rather // than the value of the bar defined outside of this scope (which // is 'false') } } Multiple modules may be named in a single ``use`` statement .. code-block:: chapel { use ModToUse, AnotherModule, ThirdModule; if (a || b < 0) { // Refers to AnotherModule.a (which is 'false') and ThirdModule.b (which // is '-13.0') writeln(foo); // Refers to ModToUse.foo } else { writeln(bar); // Refers to ModToUse.bar } // Will output ModToUse.foo (which is '12') } Multiple modules may also be named in a single ``import`` statement .. code-block:: chapel { import ModToUse, AnotherModule, ThirdModule; if (AnotherModule.a || ThirdModule.b < 0) { writeln(ModToUse.foo); } else { writeln(ModToUse.bar); } // Will output ModToUse.foo (which is '12') } And such ``import`` statements can mix and match between importing modules and the symbols within them. .. code-block:: chapel { import ModToUse.{foo, bar}, AnotherModule, ThirdModule.b; if (AnotherModule.a || b < 0) { // Refers to ThirdModule.b (which is '-13.0') writeln(foo); // Refers to ModToUse.foo } else { writeln(bar); // Refers to ModToUse.bar } // Will output ModToUse.foo (which is '12') } You can also ``import`` multiple modules in a single statement by naming a shared parent module. We will talk about this more in :ref:`Primer_Nested_Modules`. Equivalently, a scope may contain multiple ``use`` statements .. code-block:: chapel { use ModToUse; use AnotherModule, ThirdModule; writeln(a && foo > 15); // outputs false (because AnotherModule.a is 'false' and ModToUse.foo is // '12') } A scope may also contain multiple ``import`` statements .. code-block:: chapel { import ModToUse.foo; import AnotherModule.a; writeln(a && foo > 15); // outputs false (because AnotherModule.a is 'false' and ModToUse.foo is // '12') } It can even contain a mix of ``import`` and ``use`` statements .. code-block:: chapel { use ModToUse; import AnotherModule.a; writeln(a && foo > 15); // outputs false (because AnotherModule.a is 'false' and ModToUse.foo is // '12') } In any case, the modules accessed in this way are considered in concert (after symbols defined at this scope but before symbols defined outside of it) - the ordering within a ``use`` statement or across multiple ``use`` or ``import`` statements does not affect the precedence of symbols that share a name. This means that if two modules each define a symbol with the same name, and both modules are brought in at the same scope, attempts to access a symbol by that name will result in a naming conflict. The commented-out line below would fail because both ``ModToUse`` and ``Conflict`` define a symbol named ``bar``: .. code-block:: chapel { use ModToUse, Conflict; writeln(foo); // Outputs ModToUse.foo ('12') // writeln(bar); writeln(other); // Outputs Conflict.other ('5.0 + 3.0i') } When the symbol being accessed is the name of a function, the rules become more complex. If the two function definitions are overloads (or define different arguments), then the best match will be found, no matter where the function is defined relative to the other function definitions. More details on when overloading applies, when functions may shadow other functions, etc. can be found in the relevant section of the language specification. They will not be covered further in this primer. Finally, the names of the modules themselves are made available by a ``use`` statement at a scope just outside of the modules' contents and just inside the next lexical scope surrounding the current one. .. code-block:: chapel { use ModToUse, DifferentArguments; writeln(baz(2, 3)); // Accesses the function ModToUse.baz using the two arguments. Should // output 2 * (2 + 3) or '10' writeln(baz(3)); // Access the function DifferentArguments.baz using the single argument. // Should output 3 - 2, or '1' } Remember: this is in contrast to ``import`` statements, where only the module name or specific symbols within the module are brought in, rather than both. Also remember that you can write a ``use`` statement that doesn't enable accesses to the module's name, by renaming the module to '_'. Limiting a Use -------------- To get around such conflicts, there are multiple strategies. If only a small number of symbols are desired from a particular module, you can specify the symbols to bring in via an ``only`` list. Here, because of the ``only`` clause in the ``use`` of ``Conflict``, Conflict's ``bar`` is not directly accessible here. .. code-block:: chapel writeln(); writeln("Limiting a use"); { use ModToUse; use Conflict only other, another; writeln(foo); // Outputs ModToUse.foo ('12') writeln(bar); // Outputs ModToUse.bar ('2') writeln(other); // Outputs Conflict.other ('5.0 + 3.0i') } ``import`` statements for unqualified access are somewhat similar to ``only`` lists on ``use`` statements, though the syntax is different. We saw how to ``import`` a single symbol for unqualified access earlier, so this example demonstrates how to ``import`` multiple symbols for unqualified access. .. code-block:: chapel { use ModToUse; import Conflict.{other, another}; writeln(foo); // Outputs ModToUse.foo ('12') writeln(bar); // Outputs ModToUse.bar ('2') writeln(other); // Outputs Conflict.other ('5.0 + 3.0i') } Using an ``except`` list on a ``use`` statement will cause every symbol other than the ones listed to be available. .. code-block:: chapel { use Conflict; use ModToUse except bar; writeln(foo); // Outputs ModToUse.foo ('12') writeln(bar); // Outputs Conflict.bar ('5') writeln(other); // Outputs Conflict.other ('5.0 + 3.0i') } ``import`` statements do not have an equivalent to ``except`` lists. If both symbols which conflict are desired, or if the ``use`` causes symbols to be shadowed which are necessary, you can choose to rename a symbol when including it via the ``as`` keyword, so long as the new name does not cause any conflicts with other included symbols. .. code-block:: chapel { use ModToUse; use Conflict only bar as boop; writeln(bar); // Outputs ModToUse.bar ('2') writeln(boop); // Outputs Conflict.bar ('5') } Similarly, ``import`` statements allow renaming symbols within the curly braces. .. code-block:: chapel { use ModToUse; import Conflict.{bar as boop}; writeln(bar); // Outputs ModToUse.bar ('2') writeln(boop); // Outputs Conflict.bar ('5') } You can also ``use`` a module without making any symbols available in an unqualified manner using an empty identifier list after ``only``. This form is typically used by programmers who prefer to always fully qualify accesses to their modules' symbols. .. code-block:: chapel { use ModToUse only; use Conflict only; writeln(ModToUse.bar); // Outputs ModToUse.bar ('2') writeln(Conflict.bar); // Outputs Conflict.bar ('5') // writeln(bar); // this won't resolve since bar isn't available } Again, this is similar to an ``import`` of just the module itself. .. code-block:: chapel { import ModToUse; import Conflict; writeln(ModToUse.bar); // Outputs ModToUse.bar ('2') writeln(Conflict.bar); // Outputs Conflict.bar ('5') // writeln(bar); // this won't resolve since bar isn't available } Class and record instances obtained in scopes where their type is not otherwise visible can still access any fields and any methods defined in their type's original scope. To impact the visibility of methods defined in modules other than where the type was defined, known as "tertiary methods", the type itself can be listed in an ``only`` or ``except`` list for ``use`` statements, or as one of the symbols listed in an ``import`` statement. .. code-block:: chapel { use ModToUse only; var rec = new ModToUse.Rec(4); // Only accessible via the module prefix writeln(rec.field); // Accessible because we have an instance rec.method1(); // Ditto to the field case rec.method2(); use RecMoreMethods only Rec; rec.method3(); // Enabled by previous use statement } writeln(); Application to Enums -------------------- ``use`` statements can also be called on enums. Normally to access one of an enum's constants, you must provide a prefix of the enum name. With a ``use`` of that enum, such a prefix is no longer necessary. .. code-block:: chapel writeln("Application to enums"); { enum color {red, blue, yellow}; { // Normally you must prefix the constant with the name of the enum var aColor = color.blue; writeln(aColor); } { use color; // The 'use' statement allows you to access an enum's symbols without // the prefix var anotherColor = yellow; // color.yellow writeln(anotherColor); } } writeln(); All of the above rules for using modules also apply to using enums. ``import`` statements, on the other hand, do not apply to enums any more than they do to other non-module symbols - an enum can be listed for unqualified access, but doing so will not enable unqualified access to the enum's constants. .. _Primer_Nested_Modules: Nested Modules -------------- A ``use`` of a nested module (see the module ``OuterNested`` and its submodules for an example of a nested module) is similar to that of a top-level module. Its name is treated like any other visible symbol in the outer module, so if the outer module has not been used then the inner module must be explicitly named. .. code-block:: chapel { use OuterNested.Inner1; writeln(foobar); // Will output Inner1.foobar, or '14' } Similarly, in order to ``import`` a nested module, you must provide the explicit path to that module. .. code-block:: chapel { import OuterNested.Inner1; writeln(Inner1.foobar); // Will output 14 } While ``import`` statements cannot list multiple modules in the same way that ``use`` statements do, when a common parent is provided multiple submodules can be listed. .. code-block:: chapel { import OuterNested.{Inner1, Inner2}; writeln(Inner1.foobar); // Will output 14 writeln(Inner2.canSeeHidden); // Will output true } } // end of main() function } // end of MainModule module We'll use the module OuterNested to demonstrate some more details of nested modules. .. code-block:: chapel module OuterNested { var foo = 12; var bar: int = 2; private var hiddenFoo = false; proc baz (x, y) { return x * (x + y); } private proc hiddenBaz(a) { writeln(a); return a + 3; } A module defined within another module is called a nested module. These submodules cannot refer to symbols defined within their parent module, without a ``use`` or ``import`` of the parent module. The parent module can only access the contents of the nested module using a ``use`` or ``import`` statement or a fully qualified name. The variable ``foobar`` accesses OuterNested's ``foo`` and ``bar`` variables. .. code-block:: chapel module Inner1 { use OuterNested; var foobar = foo + bar; } Since the module ``Inner2`` is defined within ``OuterNested``, it can access the private variable ``hiddenFoo`` and the private function ``hiddenBaz``. However, any private symbol defined within ``Inner2`` will not be visible within scopes defined outside of ``Inner2``. .. code-block:: chapel module Inner2 { use OuterNested; private var innerOnly = -17; var canSeeHidden = !hiddenFoo; } module Inner3 { var x: int = 11; } Parent modules can ``use`` their child modules by specifying the full path to them. .. code-block:: chapel { writeln("Executing OuterNested's module-level code"); use OuterNested.Inner3; writeln(x); } But they can also ``use`` the child modules with just the name itself, since it is in scope. .. code-block:: chapel { use Inner3; writeln(x); } In contrast, ``import`` statements cannot ``import`` submodules with just the name itself. .. code-block:: chapel { import OuterNested.Inner3; //import Inner3; // Will not work writeln(Inner3.x); } However, both ``use`` and ``import`` statements can utilize ``this`` as a prefix, to avoid having to provide the full path. .. code-block:: chapel { import this.Inner3; writeln(Inner3.x); } module Inner4 { Child modules can also utilize ``super`` as a prefix, allowing access to other symbols defined on their parent module. .. code-block:: chapel import super.Inner3; writeln(Inner3.x); } // These lines are to ensure everything gets tested regularly writeln("End of OuterNested's module-level code"); { use UsesTheUser; } writeln("End of reverse file-order output"); writeln(); } // end of OuterNested module Public vs. Private Uses and Imports ----------------------------------- Use statements can be labeled as being either ``public`` or ``private``. By default ``use`` statements are ``private``. This means that if one module uses a second, it will only see the symbols defined by that module, not by other modules that it happens to use. For example, consider the following library module: .. code-block:: chapel module ModuleThatIsUsed { proc publiclyAvailableProc() { writeln("This function is accessible!"); } } And a module that uses it: .. code-block:: chapel module UserModule { use ModuleThatIsUsed; // or `private use ModuleThatIsUsed` } When a scope has a ``use`` of ``UserModule``, the symbols from ``ModuleThatIsUsed`` will not be available due to the ``private`` nature of ``UserModule`` 's ``use``, so the following code would not compile. .. code-block:: chapel module UsesTheUser { proc func1() { use UserModule; // publiclyAvailableProc(); // Won't compile, since ``UserModule``'s ``use`` is ``private`` } // These lines are to ensure everything gets tested regularly writeln("Start of UsesTheUser's module-level code"); func1(); { use UsesTheUser2; } writeln("End of UsesTheUser's module-level code"); } By contrast, a ``public use`` will permit symbols used by one module to be seen by those that use it. However, it does not provide the name of the module ``public use``'d (but note that it is possible to opt in to that with ``public import`` or with renaming the module with ``as``). For example, consider the following variation of the previous example: .. code-block:: chapel module UserModule2 { public use ModuleThatIsUsed; } Since its use is ``public``, a scope with a ``use`` of ``UserModule2`` will also be able to see the symbols defined by ``ModuleThatIsUsed``. .. code-block:: chapel module UsesTheUser2 { proc func2() { use UserModule2; publiclyAvailableProc(); // available due to ``use`` of ``ModuleThatIsUsed`` } // These lines are to ensure everything gets tested regularly writeln("Start of UsesTheUser2's module-level code"); func2(); { use UsesTheUser3; } writeln("End of UsesTheUser2's module-level code"); } In addition, ``public use`` statements `re-export` the symbols in the used module. This means that the used symbols can also be treated as though they are defined in the scope with the ``use`` for the purpose of accesses from outside that module. For example, here it will also appear as though the module ``ModuleThatIsUsed`` is a submodule of ``UserModule2``, so other scopes can also access it in that manner. .. code-block:: chapel module UsesTheUser3 { proc func3() { use UserModule2; UserModule2.publiclyAvailableProc(); // The above is available due to the ``public use`` of ``ModuleThatIsUsed`` // in ``UserModule2``. } // These lines are to ensure everything gets tested regularly writeln("Start of UsesTheUser3's module-level code"); func3(); { use UsesTheImporter; } writeln("End of UsesTheUser3's module-level code"); } Import statements can also be labeled as being either ``public`` or ``private``. By default ``import`` statements are also ``private``. However, ``public import`` statements have a different meaning than ``public use`` statements do - while ``public import`` statements still `re-export` the symbols in the imported module, the impact into the scope is more limited. This is because ``import`` statements do not enable both qualified and unqualified access to the same module in the same statement. Again, this means that the imported symbols will be treated as though they are defined in the scope with the ``import`` for the purpose of accesses from outside that module. For example: .. code-block:: chapel module ImporterModule { public import ModuleThatIsUsed; } Here, it will appear as though the module ``ModuleThatIsUsed`` is a submodule of ``ImporterModule``, so other scopes can access it in that manner. .. code-block:: chapel module UsesTheImporter { use ImporterModule; // Possible due to re-export of ModuleThatIsUsed ModuleThatIsUsed.publiclyAvailableProc(); // These lines are to ensure everything gets tested regularly { use NoMiddleMan; } } This doesn't prevent ``ModuleThatIsUsed`` from being available through its original means. .. code-block:: chapel module NoMiddleMan { use ModuleThatIsUsed; publiclyAvailableProc(); // These lines are to ensure everything gets tested regularly { use UsesTheImporter2; } } The same is true of module-level symbols brought in by ``public import`` statements. In this example, the ``public import`` of the function defined in ``ModuleThatIsUsed`` makes it appear as though ImporterModule2 defined the function. .. code-block:: chapel module ImporterModule2 { public import ModuleThatIsUsed.publiclyAvailableProc; } module UsesTheImporter2 { use ImporterModule2; writeln("Start of reverse file-order output"); // Possible due to re-export of ModuleThatIsUsed.publiclyAvailableProc ImporterModule2.publiclyAvailableProc(); }