-
Notifications
You must be signed in to change notification settings - Fork 123
ParameterizedModules
XXX: Make parameter P : S
just declare P
which can then be open
-ed (at least for the core calculus)
XXX: It may be useful to allow type constraints
at the top-level of a module---this would allow constraining type parameters from different parameters
A module is for managing names, and may:
- bring things into scope via imports (or
open
, see later) - define new entities via definitions
- a module may export some of the entities that are in scope in it
Modules may be nested.
- An entity that's in scope in a module, is also in scope in all its nested modules (i.e., the parent module is implicit imported)
- Names in nested module may shadow names in outer modules.
- A nested module declaration brings a new module entity in scope
-
import
always refers to a top-level module (found in the usual way) - We use
open
, which is similar toimport
but is used to bring names from nested modules in scope.
Example 1: Basic use
module A where // A top level (ordinary) module
module B where // A nested module declaration
x = 0x2
open B // This is OK because `B` is in scope
y = x // `x` got in scope via the import above
Example 2: Nested modules may refer to each other
module A where
module B where
x = 0x2
module C where
open B // This is OK because `B` is in scope
z = x
open C
y = z
// y = x // Error: `x` is not in scope
Example 3: Importing a nested module
module A where
module B where
x = 0x2
module C where
import qualified A // Brings `A::B` into scope
open A::B // This is the `B` imported above
- Define and open a nested module (see Example 1)
open module A where // Also a variant where the name can be omitted
...
means
module A where
...
open A
- Open a nested module (see Example 3)
import open A::B -- XXX: hm
means
import A as Anon
open Anon::B
A signature is a named set of module parameters, which may be:
- type parameters of a specific kind
- constraints on the type parameters
- value parameters, of a specific (possibly polymorphic) type
Example:
signature A where
type n : # // type parameter
type constraint (n <= 64) // constraint on type parameter
len : [n] // value parameter
A declaration like the above defines a new signature entity called A
.
- Signature declarations reside within other modules.
- The parameter names in a signature shadow names from the outer module scope.
A module may be parameterized by a signature, with a parameter declaration:
parameter P : S // `P` is the parameter name
// `S` is the name of a signature that is in scope
A parameter declaration is similar to an import
, in that it brings the
parameters from the specified signature in scope---the parameters may be used
like ordinary values or types.
Similar to imports, parameters may be brought into scope with a qualifier:
parameter P : S as Q
A declaration like this would bring the parameters from S
in scope qualified
by the prefix Q
- unnamed parameters
parameter S
means
parameter S : S
- combined parameter and signature declaration:
parameter
v : [64]
means
signature Anon
v : [64]
parameter Anon : Anon
Multiple anonymous parameter blocks are combined into a single anonymous signature and parameter.
New modules may be defined with a module instantiation declaraiont:
module A = M with { P = N1; Q = N2 }
- This declaration defines a new module entity
A
, that may be used like any other module. -
M
,N1
, andN2
are module entities-
M
must contain a parameter declarationsP : S1
andQ : S2
-
N1
should matchS1
andN2
S2
.
-
Syntactic sugar:
- omit parameter name if
M
has only a single parameter:
module A = M with N
- inline module declarations:
module A = M with
module P where
...
module Q where
...
means:
module Anon1 where
...
module Anon2 where
...
module A = M with { P = Anon1, Q = Anon2 }
- inline declarations when only a single signature parameter:
module A = M where
x = 64
means:
module Anon where
x = 64
module A = M with { P = Anon } // P is the only parameter of `M`
- combined import and instantiation
open (M where x = 64)
Instantiating a module with the same parameters gives rise to the same module. For example, consider:
module A = M with { P = N }
module B = M with { P = N }
A
and B
are two distinct module entities that refer to the same module.
This can be observed when resolving names, or with newtypes
.
Anonymous modules generate new modules distinct from everything else. Consider, for example:
module C = M where x = 64
module D = M where x = 64
C
and D
are two distinct module entities that refer to different modules, because
the desugared version is:
module Anon1 where
x = 64
module Anon2 where
x = 62
module C = M with { P = Anon1 }
module D = M with { P = Anon2 }
ModuleExpr =
Module [Decl] -- define a new module
| ModulInst ModuleExpr (Map Name ModuleExpr) -- define by instantiation
| ModuleName Name -- reference to a module that's in scope
| ModuleSig ModuleExpr Name -- Annotate a module with a signature
Decl =
...
| Signature Name [ SigParam ] -- Declare a signature
| Parameter Name Name -- param name, signature name
| Module Name ModuleExpr -- declare a new module
| Open ModuleExpr (Maybe Name) -- bring names from a nested module module in scope