A dynamically-typed, garbage-collected scripting language.

Version 0.17.7

Type Annotations

Pyro supports optional type annotations for variable, function, and method declarations, e.g.

var foo: i64 = 123;
var bar: f64 = 1.0;

def is_long(text: str) -> bool {
    return text:byte_count() > 100;

def increment(value: i64, inc: i64 = 1) -> i64 {
    return value + inc;

Pyro doesn't (currently) do anything with these type annotations, apart from verifying their syntax, but they're useful for documenting your code.

Future versions of Pyro may support optional type-checking or automatic documentation-generation using type annotations.

Builtin Types

Annotations for builtin types are as follows:

The annotations below describe interfaces rather than concrete types:

A callable value is callable; an iterable value has an :$iter() method that returns an iterator; an iterator value has a :$next() method that returns the next item from a sequence.

Type annotations beginning with a lowercase letter are reserved for language builtins.


Specify a variable's type by following its name with a colon and a type declaration, e.g.

var foo: i64;
var bar: f64 = 1.0;

If you don't specify a variable's type, its implicit type is any.

The same syntax works for function and method parameters, e.g.

def func(arg1: i64, arg2: f64 = 1.0) {

If you don't specify a parameter's type, its implicit type is any.

Functions and methods can declare their return type by following their parameter list with an arrow, ->, and a type declaration, e.g.

def func() -> str {

If you don't specify a return type, the implicit return type is any.

Builtin container types can optionally specify their content types in square brackets, e.g.

var foo: vec[str];
var bar: map[str, i64];
var baz: tup[i64, bool, str];

If you don't specify a container's content types, their implicit type is any, e.g.

var foo: vec;
var bar: map;
var baz: tup;

Where a type can be one of a discrete set of options, separate the options with a |, e.g.

var foo: i64|f64;
var bar: map[str, i64|f64];

Type declarations can be nested as required, e.g.

var foo: vec[map[str, i64|f64]];

Nullable Types

You can indicate that a type is nullable — i.e. can be either the specified type or null — by appending a ? to the type name, e.g.

var foo: i64?;
var bar: vec[str?];
var baz: map?[str, i64];

This is simply a shortcut for declaring the type as type|null.

Callable Values

You can indicate that a value is callable — i.e. that the value is a function, method, class, or callable instance — using the callable type, e.g.

var foo: callable;

You can optionally specify the callable's return type, e.g.

var foo: callable -> bool;

If you don't specify a return type, the implicit return type is any.

You can optionally specify the callable's parameter types, e.g.

var foo: callable();
var bar: callable() -> bool;
var baz: callable(i64, str);
var bam: callable(i64, str) -> bool;

The callable type is nullable, e.g.

var foo: callable?;
var bar: callable?(i64) -> bool;

User-Defined Types

A type name should specify either one of the builtin types or a user-defined type, possibly imported from a module, e.g.

var foo: UserType;

Here, foo is an instance of the class or member of the enum UserType. Type names can include a module path, e.g.

var bar: mod1::mod2::UserType;

Here, bar is an instance of the class or member of the enum UserType defined in the module mod1::mod2.

Type Aliases

You can define an alias for a type using a typedef statement, e.g.

typedef Token tup[str, i64, bool];

def makes_token() -> Token {
    return $tup("FOO", 123, true);

Variadic Functions

A type annotation in a variadic function specifies the type of the individual parameter values, e.g.

def add(*args: i64|f64) {
    return args:iter():sum();

assert add(1, 2.0) == 3.0;

Here, add() is a function that takes a variable number of arguments; each argument can be an i64 or an f64.