A dynamically-typed, garbage-collected scripting language.

Version 0.10.11


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.

Note that only the final module name in the import chain is declared as a variable in the importing scope, e.g.

import foo::bar::baz;

In this case, only baz is declared as a variable in the importing scope.

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.

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.

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 is called.)


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, e.g.

$ pyro path/to/module/directory

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

Importing using $exec()

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

var code = $read_file("path/to/file.pyro");
var mod = $exec(code);

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

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

Importing using $import()

You can import a module using the $import() function which takes the module's import path as a string, e.g.

var mod = $import("std::math");
assert mod::abs(-1) == 1;

You can wrap the $import() function in a try expression to check if a module exists, e.g.

var mod = try $import("foo::bar::baz");

if $is_err(mod) {
    echo "not found";