Modules, interfaces and implementations

The need to have two modules reference one another often occurs when building systems. In OmniMark solves this problem by providing a mechanism to split a module into an interface definition module and an implementation module. The interface module contains all of the exported definitions and the implementation module contains the execution code that implements the exported functionality. This division of the module allows users to have modules that directly reference or import one another's definitions. The following example shows a module declaration for an interface:

  ; points.xif
  module interface 
     shared as "points.xif"
  export record point
     field integer x
     field integer y
  export point function 
     plot  (value integer x, 
            value integer y) 

The implementation of this module might read like this:

  ; points.xmp
     implements "points.xif"
  export record point
     field integer x initial { 0 }
     field integer y initial { 0 }
  export point function 
     plot  (value integer x, 
            value integer y)
     local point p
     set p:x to x
     set p:y to y
     return p

The standard suffix for a module interface file is .xif (pronounced zif), while the standard suffix for a module implementation file is .xmp (pronounced chimp).

This example demonstrates how interface and implementation modules are to be used:

  ; main.xom
  import "point.xif" unprefixed
     local point p
     set p to point (7, 12)
  import "point.xmp" unprefixed

To use the split modules, you have to include both of them in your program like this:

  import "point.xif" unprefixed
  import "point.xmp" unprefixed

It is not necessary to have them one after the other, but they both must be imported. The interface module must be imported before the first use of one of the exported types, variables or functions occurs.

An interface module can contain:

Only shared modules can be split into interfaces and implementation modules.

An interface module cannot contain code or initial values. Therefore, it cannot contain

  • function bodies,
  • initial values for constants or global variables,
  • initial values for optional function arguments,
  • initial values for record fields, or
  • imports of implementation modules, or combined modules.

The implementation module contains the definitions for everything defined in the interface file, as well as the definitions of any private things that it may require. An implementation module can only export those things declared in its interface module. It is not permitted to extend the interface in any way.

Interface and implementation modules solve the problem of mutually-recursive imports, just like function pre-definitions solve the problem of mutually-recursive functions.