Skip to content

Packages

Introduction

Not all of the NPL code used for one particular protocol needs to be in the same file. NPL provides the concept of packages and uses to include entities from other NPL files.

Every file is contained within a package, specified by the package statement, which must go first in a file. A package name is a dot separated list of identifiers, for example com.noumena.bank.iou. A missing package directive implies an empty package name.

It is strongly recommended, although not enforced, that all files in a package are contained in a single folder corresponding to the package name, relative to the module source root. In the above example, this would likely be src/main/npl/com/noumena/bank/iou.

All symbols within a single package are automatically visible, hence the following is valid:

File inclusion/a/Foo.npl:

package inclusion.a

protocol[party] Foo() {};

File inclusion/a/Bar.npl:

package inclusion.a

protocol[party] Bar(f: Foo) {};

This implies that the same symbol cannot be defined in multiple files in the same package. Attempting to define Foo in /inclusion/a/Bar.npl therefore produces a compiler redefinition error.

Use statement

To import a symbol from a different package, the use statement is employed. The use statement takes a single argument – the fully qualified name of the imported symbol.

File inclusion/a/Foo.npl:

package inclusion.a

protocol[party] Foo() {};

File inclusion/b/Bar.npl:

package inclusion.b

use inclusion.a.Foo

protocol[party] Bar(f: Foo) {};

These types of symbols can be imported:

  • protocols
  • symbols
  • structs
  • identifiers
  • unions
  • constants
  • functions (except for member functions)

These types of symbols cannot be imported:

  • struct fields
  • protocol fields and members
  • protocol member functions
  • protocol states
  • actions
  • local variables and function parameters

Note that fields, members and actions can be used without an explicit import, as they are inherently bound to the parent type. For example, one does not need to import inclusion.a.SomeStruct.x in:

File inclusion/a/Foo.npl:

package inclusion.a

struct SomeStruct { x: Number };

File inclusion/b/Bar.npl:

package inclusion.b

use inclusion.a.SomeStruct

function useStruct(s: SomeStruct) returns Number -> {
    return s.x;
}

Prototypes

The prototypes are derived from package name instead of file name. Thus, the prototype name of SomeStruct would be /inclusion/a/SomeStruct (rather than /inclusion/a/x.npl/SomeStruct).