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 {
    Shape::Circle -> Shape::Circle::area x;
    Shape::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 = ...
  template Sale(item, price, buyer) = ...

  val income = ...
}

module Purchase {
  type Purchase: Event {}
  val stock = ...
  template 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, also when referring to other declarations inside the same module. In the example above, we wrote Shape::Rectangle and Shape::Circle inside the module Shape to refer to the circle and rectangle types, for example.
  • Modules do not provide any isolation or restrict access: Any value/type/contract defined in a module is accessible with the full module path.