Modules
Modules provide a way to structure contract specifications into blocks.
An example of a module is to group related constants such as VAT rate, payment grace period, etc. to make the contract easier to read.
Modules are specified using the module
keyword:
module Constants {
val vat = 0.25
val paymentGrace = 10 // days
}
To use declarations in a module, the ::
syntax is used:
val a = Constants::paymentGrace
// a is 10
Modules can be nested into other modules:
type BaseShape {}
module Shape {
type Circle : BaseShape {
radius : Float
}
module Circle {
val pi = 3.14159
val area = \(c : Shape::Circle) -> c.radius * c.radius * Shape::Circle::pi
}
type Rectangle : BaseShape {
length : Float,
width : Float
}
module Rectangle {
val area = \(r : Shape::Rectangle) -> r.length * r.width
}
// Compute area for any Shape
val area = \(s : BaseShape) ->
type x = s of {
Circle -> Shape::Circle::area x;
Rectangle -> Shape::Rectangle::area x;
_ -> 0.0
}
}
Notice how values are accessed in modules using the ModuleName::value
notation.
It is also possible to define contracts in modules:
module Sale {
type Sale: Event {}
val inventory = ...
contract sale = \item -> \price -> \buyer -> ...
val income = ...
}
module Purchase {
type Purchase: Event {}
val stock = ...
contract purchase = \item -> \price -> \seller -> ...
val expenses = ...
}
Some things to consider when working with modules:
You must always use the full module path to refer to declarations, except when referring to other declarations inside the same module. In the example above, we wrote
Circle
andRectangle
to refer to the circle and rectangle types, butShape::Rectangle::area
andShape::Circle::area
were required to refer to thearea
functions due to them not being defined in exactly the same module.Modules do not provide any isolation or restrict access: Any value/type/contract defined in a module is accessible with the full module path.