A scripting language for people who enjoy the simpler things in life.

Version 0.9.35


A module is a Pyro file loaded as a library. Modules are loaded using the import keyword.

Assume we have a file called math.pyro containing math functions:

import math;

The imported module's name becomes a variable in the importing scope:

assert math::abs(-1) == 1;

Use the member access operator :: to access a module's top-level members — i.e. variables, functions, and classes.

Public/Private Members

A module's top-level members (variables, functions, and classes) are private by default — use the pub keyword to make them public, e.g.

pub var number = 123;

pub def func() {
    return "hello world!";

pub class Object {
    var value;


Modules can contain submodules. Assume we have a directory called math containing a file called trig.pyro:

import math::trig;

var value = trig::cos(1);

The top-level module math can live alongside the directory that contains its submodules:

|-- math.pyro
|-- math/
    |-- trig.pyro

Alternatively, it can live inside the math directory in a file called self.pyro:

|-- math/
    |-- self.pyro
    |-- trig.pyro

Finally, the top-level math module can be empty and simply function as a container for its submodules:

|-- math/
    |-- trig.pyro

Submodules can contain submodules of their own — there's no hard limit to how deep the nesting can go.

Parents, Children, Siblings

Imagine we have the following module structure:

|-- math/
    |-- calc.pyro
    |-- self.pyro
    |-- trig.pyro

A parent module can import its child – e.g. in root/math/self.pyro:

import math::trig;

A child module can import its parent – e.g. in root/math/calc.pyro:

import math;

A child module can import its sibling – e.g. in root/math/calc.pyro:

import math::trig;

Module Caching

A module file is only executed the first time it's imported. The imported module is cached and any subsequent imports simply load the module object from the cache.

Importing a submodule automatically executes and adds its ancestor modules to the cache if they haven't already been imported. For example, given the statement:

import foo::bar::baz;

Only the final module name in the chain is declared as a variable in the importing scope — here baz.

Circular Imports

In general, circular imports are fine — module foo can import module bar and module bar can import module foo.

Note that trying to use a member from an imported module that hasn't finished initializing can result in a panic.

Note that you can avoid the problems that sometimes result from top-level circular imports by importing the required module inside a function, e.g.

def do_some_math(value) {
    import math;
    return math::abs(value) * math::pi;

This import will only fire when the function is run. (Imported modules are cached so the module won't be re-imported every time the function runs.)


You can import a module under an alias using an import ... as ... statement:

import math as alias;

var foo = alias::abs(-1);

Submodules can similarly be aliased:

import math::trig as alias;

var foo = alias::cos(1);

Importing Members

You can import a top-level member from a module by wrapping its name in braces, e.g.

import math::{abs};

assert abs(-1) == 1;

You can alias the imported member using an import ... as ... statement, e.g.

import math::{abs} as foo;

assert foo(-1) == 1;

You can import multiple members in a single import statement by separating their names with commas, e.g.

import math::{cos, sin};

You can alias the imported members using a comma-separated as list, e.g.

import math::{cos, sin} as foo, bar;

You can import all the top-level members from a module using an asterisk, e.g.

import math::{*};

Note that you can only use this {*} syntax at global scope in a script or module file — i.e. you can't import {*} inside a block or function.

Import Roots

The global $roots vector contains the list of root directories that Pyro checks when attempting to import a module.

To customize Pyro's import behaviour:

Executing Module Directories

You can execute a module directory as a script if it contains a self.pyro file:

$ pyro path/to/module/directory

Pyro executes the self.pyro file, then the $main() function if it finds one.

Importing Files/Strings

You can import any file as a module using the $exec() function, which executes a string of Pyro source code as a new module, e.g.

var source_code = $read_file("path/to/file.pyro");
var module = $exec(source_code);

Unlike the import statement, this doesn't add the new module to the cache.

You can also use the $exec() function to create a new module directly from a string, e.g.

var module = $exec(`pub def func() { return "foobar"; }`);
assert module::func() == "foobar";