Chapter 6. Module

A module is a scope that contains declarations of types (Chapter 3), extents (Section 4.2), and computed values (Section 4.1). Modules override lexical scoping to import symbols that have been exported from another module.

Compilation Unit

Several modules may be contained within a CompilationUnit, typically a text file.

CompilationUnit:

 

ModuleDeclarationList

ModuleDeclarationList:

 

ModuleDeclaration

 

ModuleDeclarationList ModuleDeclaration

Module Declaration

A ModuleDeclaration is a named container/scope for type declarations, field declarations, and computed value declarations.

ModuleDeclaration:
  module QualifiedIdentifer ModuleBody  ;opt
QualifiedIdentifier:
  Identifier
  QualifiedIdentifier . Identifier
ModuleBody:
  { ImportDirective* ExportDirective* ModuleMemberDeclaration* }
ModuleMemberDeclaration:
  FieldDeclaration
  ComputedValueDeclaration
  TypeDeclaration

Each ModuleDeclaration has a QualifiedIdentifier that uniquely qualifies the declarations contained by the module.

Each ModuleMemberDeclaration may be referenced either by its Identifier or by its fully qualified name by concatenating the QualifiedIdentifier of the ModuleDeclaration with the Identifier of the ModuleMemberDeclaration (separated by a period).

For example, given the following ModuleDeclaration:

module PeopleData {
    Names : Text*;
}

the fully qualified name of the field is PeopleData.Names, or using escaped identifiers, [PeopleData].[Names]. It is always legal to use a fully qualified name where the name of a declaration is expected.

Modules are not hierarchical or nested. That is, there is no implied relationship between modules whose QualifiedIdentifier share a common prefix.

For example, consider these two declarations:

module A {
    N : Number;
}
module A.B {
    NPlusOne : { N + 1 }
}

Module A.B is in error, as it does not contain a declaration for the identifier N. That is, the members of Module A are not implicitly imported into Module A.B.

Inter-Module Dependencies

M uses ImportDirectives and ExportDirectives to explicitly control which declarations may be used across module boundaries.

ExportDirective:
  export Identifiers ;
ImportDirective:
  import ImportModules ;
  import QualifiedIdentifier { ImportMembers } ;
ImportMember:
  Identifier  ImportAliasopt
ImportMembers:
  ImportMember
  ImportMembers , ImportMember
ImportModule:
  QualifiedIdentifier  ImportAliasopt
ImportModules:
  ImportModule
  ImportModules , ImportModule
ImportAlias:
  as Identifier

A ModuleDeclaration contains zero or more ExportDirectives, each of which makes a ModuleMemberDeclaration available to declarations outside of the current module.

A ModuleDeclaration contains zero or more ImportDirectives, each of which names a ModuleDeclaration whose declarations may be referenced by the current module.

A ModuleMemberDeclaration may only reference declarations in the current module and declarations that have an explicit ImportDirective in the current module.

An ImportDirective is not transitive; that is, importing module A does not import the modules that A imports.

For example, consider this ModuleDeclaration:

module People.Types {
    export Person;

    SecretNumber : Number;
    type Person  { FirstName : Text; Age : Number; }
}

The field People.Types.SecretNumber may only be referenced from within the module People.Types. The type People.Types.Person may be referenced in any module that has an ImportDirective for module People.Types, as shown in this example:

module People.Data {
    import People.Types;
    export Names;

    Names : Text*;
    Friends : People.Types.Person*;
}

The preceding example used the fully qualified name to refer to People.Types.Person. An ImportDirective may also specify an ImportAlias that provides a replacement Identifier for the imported declaration:

module People.Data {
    import People.Types as pt;
    export Names;

    Names : Text*;
    Friends : pt.Person*;
}

An ImportAlias replaces the name of the imported declaration. That means that the following is an error:

module People.Data {
    import People.Types as pt;
    export Names;
     Names : Text*;
     Friends : People.Types.Person*;
}

It is legal for two or more ImportDirectives to import the same declaration, provided they specify distinct aliases. For a given compilation episode, at most one ImportDirective may use a given alias.

If an ImportDirective imports a module without specifying an alias, the declarations in the imported module may be referenced without the qualification of the module name. That means the following is also legal:

module People.Data {
    import People.Types;
    export Names;

    Names : Text*;
    Friends : Person*;
}

When two modules contain same-named declarations, there is a potential for ambiguity. The potential for ambiguity is not an error—ambiguity errors are detected lazily as part of resolving references.

Consider the following two modules:

module A {
    export X;
    X : Number;
}
module B {
    export X;
    X : Number;
}

It is legal to import both modules either with or without providing an alias:

module C {
    import A, B;
    Y { 1 + 2 }
}

This is legal because ambiguity is only an error for references, not declarations. That means that the following is a compile-time error:

module C {
    import A, B;
    Y { X + 2 } // error: unqualified identifier X is ambiguous
}

This example can be made legal either by fully qualifying the reference to X:

module C {
    import A, B;
    Y { A.X + 2 } // no error
}

or by adding an alias to one or both of the ImportDirectives:

module C {
    import A;
    import B as bb;
    Y { X + 2 } // no error, refers to A.X
    Z { bb.X + 2 } // no error, refers to B.X
}

Because module names may contain periods, there is a potential ambiguity when module names share a common prefix. Consider these two modules:

module A {
    export Z, B;
    type Z { C : Number; }
    B : Z;
}
module A.B {
    export C;
    C : Number;
}

If a module imports both of these modules, the QualifiedIdentifier A.B.C is inherently ambiguous, as it could either refer to the C field in module A.B or to the C field of the B field of module A. To disambiguate, one must use an alias to break the tie:

module F {
    import A;
    import A.B as ab;
    G { ab.C } // returns the C field of module A.B
    H { A.B.C }  // returns the C field of the B field of module A
}

An ImportDirective may either import all exported declarations from a module or only one of them. The latter is enabled by specifying an ImportMember as part of the directive. For example, Module Plot2D imports only Point2D and PointPolar from the Module Geometry:

module Geometry {
    export Point2D, Point2DPolar, Point3D;
    type Point2D { X : Number; Y : Number; }
    type Point2DPolar { R : Number; T : Number; }
    type Point3D : Point2D { Z : Number; }
}
module Plot2D {
    import Geometry {Point2D, Point2DPolar};
    Points : Point2D*;
    PointsPolar : Point2DPolar*;
}

An ImportDirective that contains an ImportMember only imports the named declarations from that module. This means that the following is a compilation error because module Plot3D references Point3D, which is not imported from module Geometry:

module Plot3D {
    import Geometry {Point2D};
    Points : Point3D*;
}

An ImportDirective that contains an ImportTarget and an ImportAlias assign the replacement name to the imported type, field, or computed value declaration.

Compilation Episode

Multiple compilation units may contribute declarations to a module of the same name.

The types and computed values of a module are sealed by a compilation episode. A subsequent compilation episode may not contribute additional types or computed values. Initial values for module level field declarations may be contributed in subsequent compilation episodes.

Each fragment must explicitly import the symbols used within that fragment and may only export symbols defined within that fragment.

Storage

All dynamic storage in M is modeled as module-scoped FieldDeclarations called extents. The declaration of an extent may be spread across multiple sections of program text. Consider the following example:

// catalog.m
module Catalog {
    type Product {
        Name : Text;
        Price : Decimal9;
        Product(Name,Price);
    }

    Products : Product*;
}

// groceries.m
module Catalog {
    Products {
        Product("Soap", 1.29),
        Product("Tuna", 2.49)
    }
}

// hardware.m
module Catalog {
    Products {
        Product("Lightbulb", 0.99),
        Product("Screwdriver", 5.99)
    }
}

The resulting Products extent will contain:

{
     Product("Soap", 1.29),
     Product("Tuna", 2.49),
     Product("Lightbulb", 0.99),
     Product("Screwdriver", 5.99)
}

The mapping of module-scoped FieldDeclarations to physical storage is implementation-specific and outside the scope of this specification.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset