Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

11. Modules

A Korrin program is one or more modules. Each .kor file is a module; the import statement (§5.12) loads another module and binds it as a value. This section is the normative description of loading, module objects, and the standard library (ADR 0018, ADR 0019).

11.1 Specifiers and resolution

import_statement = "import" , STRING , [ "as" , NAME ] ;

The string is the specifier. Its first characters decide how it resolves:

  • ./… or ../… — a file, resolved relative to the directory of the file containing the import (not the process's working directory, not the entry file). If the specifier has no extension, .kor is appended. A file that does not exist or cannot be read is E0314.
  • anything else — a built-in module (§11.4), looked up by exact name. No filesystem is consulted, so import "math" is always the standard library's math. An unknown name is E0314.

A relative import requires a file on disk to resolve against. When a program is run from memory rather than a file (an embedder's in-process call), a relative import is E0314; built-in imports still work.

11.2 The bound name

import "spec" binds the specifier's final path component, with any extension removed: import "./a/config" binds config, import "math" binds math. import "spec" as name binds name instead. If there is no as and the derived name is not a valid identifier (§1.5), the import is E0108 and must use as.

The specifier must be a plain string literal; one with interpolation holes is E0108.

11.3 Module objects

Evaluating an import runs the module once and produces a module object. type() reports "module".

  • Isolation. A module's file runs in its own scope — a fresh global with the built-in functions and Error, then the module's own top level. It does not see the importing program's names, and vice versa.
  • Caching. Loading is cached by canonical path (files) or name (built-ins). Importing the same module in two places yields the same object; == on module objects is identity.
  • Members. module.name reads name from the module's top-level bindings. A name that is not bound is E0309.
  • Privacy. A top-level name beginning with _ is private: reading it through the module object is E0309, even though it is bound. Code within the module uses its own _-names normally.

A module object is not callable (E0302), not indexable (E0308), and not iterable.

import "math"
print(type(math))
print(math.pi > 3)
# => module
# => true

11.4 The math module

import "math". Numeric helpers and two constants. math is float-flavoured: sqrt and pow return float; the rounding functions return int.

MemberResult
math.sqrt(x)the square root of x as a float; E0304 if x < 0
math.pow(base, exp)base raised to exp, as a float
math.floor(x)the largest int not greater than x; E0304 if it does not fit int
math.ceil(x)the smallest int not less than x
math.round(x)x rounded to the nearest int, halves away from zero
math.abs(x)the absolute value of x, keeping its type (int or float)
math.min(a, ...)the smallest of one or more numbers, returned unchanged
math.max(a, ...)the largest of one or more numbers, returned unchanged
math.piπ, as a float
math.ee, as a float
import "math"
print(math.sqrt(49))
print(math.floor(2.9))
print(math.abs(-7))
print(math.max(3, 8, 1))
# => 7.0
# => 2
# => 7
# => 8

11.5 The io module

import "io". Filesystem access and command-line arguments (ADR 0020). Every operation that can fail raises E0316, which try can catch. Paths are used as given — a relative path is relative to the process's working directory, not the script's location. There is no sandbox.

MemberResult
io.read_file(path)the file's contents as a str; E0316 if it cannot be read or is not UTF-8
io.write_file(path, text)writes text to path, creating or truncating it; returns nil; E0316 on failure
io.args()a list of the command-line arguments after the script name, as strs (empty when run from an embedder)
import "io"
try:
    text = io.read_file("does-not-exist")
except err:
    print(err.code)
# => E0316

To read a line from standard input, use the input builtin.

11.6 Cycles

While a module is loading, its path is recorded as "in progress". An import that resolves to a module already in progress is E0315 — Korrin never exposes a partially-initialised module. Move the shared definitions into a third module that both import.

11.7 Compilation errors in imported modules

A lexer, parser, or resolver error in an imported file is reported as a single E0314 at the import site, carrying the imported module's own error text. A runtime raise or fault from a module's top level propagates to the importer as an ordinary exception, catchable with try around the import.