Writing system packages

Using existing system packages covers most use cases in application development. However, by writing your own system packages, you can achieve the following:

  • Define your own system interfaces of functions and types with side effects.
  • Link applications in arbitrary file formats.

This page assumes that you have already read Packages.

Caveat: Providing bad system packages which do not conform to conventions can break the ecosystem of the language! In the worst cases, they might make applications malfunction. Please be careful to follow the conventions to keep applications maintainable and portable.

Functionalities of system packages

System packages have the following functionalities:

  • Define context types.
  • Provide system interfaces as functions and types.
  • Link application files.

Defining context types

Every system package must have a module named Context at the top level. The module defines a Context type and an UnsafeNew function that returns a Context value with no argument.

For example, a system package for command line applications might have the following Context module:

...

type Context {
  print \(string) none
}

UnsafeNew = \() Context {
  Context{
    print: ...
  }
}

The language's compiler uses these type and function to compose a context type passed to main functions in main modules in application packages.

Providing system functions and types

System packages are the only places where you can define functions that have side effects. Thanks to system packages, applications can perform effects to interact with the world, such as:

  • Console input/output
  • File system operations
  • Networking
  • Random number generation

Note that system packages should never expose side effects directly through their functions; all public functions in system packages must be purely functional. Instead, you need to pass a Context type to every effect-ful function for it to make side effects.

For example, a system package for command line applications might have the following types and functions:

# Define a foreign function to output a string in console.
import foreign _pen_cli_print \(string) none

type Context {
  print: _pen_cli_print,
}

Print = \(ctx Context, s string) none {
  ctx.print(s)
}

rather than:

import foreign _pen_cli_print \(string) none

Print = \(s string) none {
  # Oh, no! We make side effects in a public function directly.
  _pen_cli_print(s)
}

Linking application files (optional)

System packages might have optional script files named pen-link at their top directories. On every build of application packages using the system packages, the script files run given object files specified as command line arguments to link application files. The script files may or may not have file extensions.

The scripts should accept the following command line arguments.

ArgumentRequiredDescription
-t <target>NoTarget triple
-o <application>YesPath of an application file
<archive>...YesPaths of archive files sorted topologically from main packages

At the liking phase, compiled main functions are available under a symbol named _pen_main with Pen's native calling convention.

Examples

The Os standard package is an example of system packages.