module

Summary

Declares an importable module. Modules enable you to organize code and prevent naming conflicts.

Syntax

module name1[[.name2]... .name3] {
    member1;
    member2;
    member3;
}

Parameters

nameN
The name for the module. It can be any valid identifier.
memberN
A valid module member. This can be a class, interface, enum, external declaration, functions, or another module.

Description

The module keyword declares an importable module. Syntactically, modules can only be declared at the top-level scope or inside another module. When a module is a member of another module, it is known as a "submodule".

Modules are useful for code organization and re-use.

Module members do not need to be explicitly exported. All module members are automatically exported. In order to control access and visibility, use access modifiers.

Module members are implicitly "static".

Module Files

A file that declares a module can only contain import statements, external statements, and module statements.

Valid Module File:

1
2
3
4
5
6
import System; // scoped to the module file only
external jQuery; // scoped to the module file only
 
module Foo.Bar
{
}

Invalid Module File:

1
2
3
4
5
int x = 1; // A file that declares a module cannot contain variable declarations
 
module Foo.Bar
{
}

Submodules

There are two ways to declare submodules.

The first is to declare a module inside another module:

1
2
3
4
5
6
module Foo
{
    module Bar
    {
    }
}

The other way to declare a submodule is by declaring a single module with each distinct module name separated by the dot (.) operator:

1
2
3
module Foo.Bar
{
}

Externals and External Modules

Modules can also declare externals. When the module is imported, all externals declared with the module will be automatically declared inside the importing file.

The external keyword can only be used at the top-level scope of a file or inside a module. Note that there is a difference between externals declared at the program level (top-level scope) and externals declared at the module level. Externals declared at the program level are scoped to the file, while externals declared inside a module will be exported with the module. Thus, modules that wish to utilize externals without exporting them should have the externals declared at the program level scope of the module file.

1
2
3
4
5
6
external qux; // This will NOT be exported with the module
 
module Foo
{
    external bar; // This will be exported with the module
}

Modules that only export external statements are known as "External Modules".

Directory Structure

Please note that JS++ modules do not need to match the directory structure. However, it is still advisable to match the directory structure. Thus, a module Foo.Bar.Baz would be in the path "Foo/Bar/Baz.jspp".

Access Modifiers and Implicit Imports

Each module member can be declared with access modifiers to control access and visibility. By default, module members are public.

A submodule can access any other submodule or module member belonging to the same root module without explicitly importing depending on the access modifier of the module member. In this sense, you can think of the module keyword as also implicitly importing members of its parent and ancestor modules.

Module members are exported to other modules as follows:

Access Modifier Description
publicAll other modules can access, current module can access, nested modules can access, and children can access.
protectedCurrent module can access, nested modules can access, all children can access.
privateOnly current module can access.

Here are some illustrative examples for how access modifiers and implicit module imports work: (Pretend that all of the below are stored in distinct module files all in the same directory)

1
2
3
4
5
module Foo
{
    // We can use private, protected, and public members of 'Foo' without full
    // qualification here
}
1
2
3
4
5
6
7
8
9
module Foo.Bar
{
    // We can use members of 'Foo.Bar' without full qualification here.
 
    // We cannot use public members of 'Foo' without full qualification here.
 
    // However, we do not need to explicitly import 'Foo'. We can still access
    // public and protected members of 'Foo' with full qualification.
}
1
2
3
4
5
6
7
8
9
module Foo
{
    // Implicitly imports public "Foo.Bar"
}
 
module Baz
{
    // CANNOT access "Foo.Bar" here because it was implicitly imported. Use an explicit import.
}
1
2
3
4
5
6
7
8
9
module Foo
{
    private void bar() {}
 
    module Baz
    {
        // Can access "Foo.bar" but not other private members of "Foo" defined in other files
    }
}

Except for the last case, where Foo.bar is made available in Baz from its containing scope Foo, all members of the current module can be partially-qualified and are implicitly "imported". Note that names of members of ancestors and descendents of the current module cannot be partially-qualified and are not implicitly imported; however, they can still be accessed via full qualification.

See Also

Share

HTML | BBCode | Direct Link