Skip to main content

Riz

Riz is a statically typed, interpreted, and embeddable programming language with Python interoperability.

Aim

Riz is intended to be suitable as a child's first programming language—likely while programming a robot—and capable enough that they never have to grow out of it.

That requires a careful compromise between a beginner-friendly language, a general-purpose language, a microcontroller language, and an extension language. No one of those roles wins automatically. When they pull in different directions, Riz should make the trade-off that leaves the language more trustworthy.

Embedding is useful in its own right, but it is also an architectural discipline: an embeddable language needs modularity, explicit ownership, and clear boundaries between the language, runtime, and host. A strong extension API should provide an intentional path all the way down to the machine, much as CPython's extension API does, without making low-level concerns part of ordinary Riz programming.

Design values

Trustworthy defaults

Riz should do the correct thing first and make approximations or lower-level optimizations available when a programmer intentionally reaches for them. Floating-point arithmetic is valuable and fast, for example, but it should not be the incidental default when exact arithmetic is possible. The path from the simple, correct model to expert control should be gradual and understandable.

Static certainty without annotation ceremony

Runtime type discovery is too late. Riz should know types statically, while inferring enough of them that programmers usually do not need to write them out. Advanced type annotations may constrain or document a program, but they should not compensate for types inferred from callers or for uncertainty deferred until execution.

Immutability first

Riz's preferred types are immutable. Mutable values will exist, including through foreign-language interoperability, but mutation should be something programmers choose intentionally rather than encounter incidentally. Transparent conversion across a host boundary is welcome when the meaning is unambiguous; it must not quietly give mutable host objects immutable Riz value semantics.

Both errors and results

Riz needs both catchable exception-style errors and Result values. Result types are excellent for modeled failures that callers can reasonably understand and handle in type-safe ordinary control flow. They cannot enumerate every unexpected or unexpectable failure without burdening every function and distorting its useful type. Robust programs still need boundaries that can catch such failures: the unexpected should itself be expected. Making every failure exceptional is equally mistaken because it hides outcomes callers should normally consider. The precise boundary is still being designed, but neither mechanism should be forced to stand in for the other.

One language as programs grow

Beginner accessibility must not come from a simplified model that later has to be discarded. The same semantics should remain useful as a program grows from a first robot exercise into a larger application or an extension backed by native code.

Useful reference points

Riz draws direction from several languages without trying to reproduce any one of them:

  • Python as a starting point for beginner friendliness and access to its enormous ecosystem, not as a syntax or semantics constraint.
  • Rust for treating type safety as a primary constraint.
  • Clojure for an immutability-first mental model.
  • Lua for embeddability and a small, clear host boundary.
  • MicroPython for practical programming on microcontrollers.

Familiar Python syntax is useful when it serves Riz, but familiarity is not a design goal strong enough to preserve a less trustworthy or less coherent choice.

Zig is the intended long-term implementation language for the core runtime. It offers direct C interoperability and control over allocation and I/O without requiring those mechanisms to leak into ordinary Riz source.

Implementation direction

The current implementation is written in Python. It is both a usable embedding surface and a place to prove language semantics and runtime boundaries. Python support is permanent and likely critical to adoption, but the core should eventually be implemented in Zig. CPython and MicroPython bindings should then be optional adapters over the same embedding interface being developed here.

There is deliberately no fixed threshold yet for beginning the Zig implementation. Python work should prove durable language or embedding contracts and avoid large investments whose value exists only in this interpreter.

Riz is still experimental and does not carry compatibility or deprecation layers. Once a design improvement is accepted, change the language and embedding API directly, update their tests and examples, and remove the superseded design.

Current direction

The implementation currently establishes these pieces of the model:

  • Function types are inferred from definitions rather than specialized from their callers.
  • Exact integers and ratios are ordinary numeric values.
  • Products and destructuring are foundational language mechanisms.
  • Option and Result variants support exhaustive matching.
  • Host modules are typed, lazy, and independent of a filesystem layout.
  • Python interoperability is explicit at the foreign boundary.
  • A Runtime can define values, native functions, and host modules.
  • A Computation is a unique, mutable, opaque execution machine.
  • Computations report three nonterminal or terminal states: Yielded, Suspended(request), and Finished(result).
  • Ordinary Riz functions may suspend through nested calls without a distinct async function type or visible async/await boundary.
  • Native callbacks return Ok(value), Err(error), or Suspend(request).
  • Execution policy belongs outside the runtime. A small asyncio driver already demonstrates resolving opaque suspension requests without coupling the core runtime to Python's event loop.

Statement-level yielding is currently a deliberately simple mechanism for proving resumable execution. It is not intended to be the final scheduling or preemption policy.

Important open questions

These areas are intentionally unsettled. Existing experiments and discussions are evidence, not permission to choose their final design unilaterally:

  • Result propagation syntax and whether successful values are ever lifted implicitly.
  • The boundary between exception-style errors and Result values, including exception handling, cancellation, and exception injection into suspended computations.
  • Spawning computations and the eventual concurrency API. Riz wants spawning, not arbitrary cloning or forking of execution state.
  • User-defined types, generics, traits, and explicit concrete type annotations.
  • Riz-defined modules, exports, package-internal imports, and eventual filesystem conventions.
  • The ownership and identity rules for mutable and foreign values.
  • The point at which further Python development should give way to the Zig core.
  • The final balance between exact correctness and explicitly selected machine representations.

REPL polish, a broad standard library, and a complete scheduler are not current prerequisites for proving the language and embedding model.

Guidance for contributors and agents

Details matter in Riz. Syntax, core type semantics, dependencies, and public embedding APIs can all commit the language to a direction that is difficult to reverse. Do not finalize those decisions without explicit maintainer input.

Exploration is encouraged. Research prior art, compare alternatives, write tests, and build reversible prototypes to obtain real evidence. Keep prototypes isolated and the working tree clean enough that useful work can be separated from discarded ideas. Clearly distinguish an experiment from an accepted design.

When choosing a useful next project:

  1. Start from a gap at the current language/runtime frontier, not from generic feature completeness.
  2. Do not privilege language semantics, embedding, MicroPython, or Zig preparation categorically. Weigh each opportunity against the project principles, how quickly it can produce useful evidence, and its risk of leading into a dead end.
  3. Prefer work that proves a semantic or embedding contract shared by Python, Zig, CPython, and MicroPython.
  4. Favor correctness, static knowledge, immutable defaults, and explicit host boundaries.
  5. Avoid building Python-specific machinery unless it validates a portable interface or delivers lasting Python interoperability.
  6. Use small executable examples and tests to expose design consequences.
  7. Bring consequential alternatives and the evidence from prototypes back to the maintainer before settling the public design.

Development

Run a UTF-8 Riz file as a script with:

uv run riz example.riz

For example, save this as example.riz:

fn half(n): n / 2
print("Hello from Riz!")
half(5)

The script runs as one complete program in a fresh runtime. Its final value is discarded; only explicit print calls produce output. print(text) is an ordinary built-in function taking exactly one String, appending one newline, and returning Unit. There is no implicit string conversion. Blank files are a successful no-op. Errors go to stderr and produce a nonzero exit status. uv run python -m riz example.riz works too.

Run the interpreter shell with:

uv run riz

Run the test suite and static checker with:

uv run pytest
uv run basedpyright

An experimental browser shell lives in web. It runs the same interpreter in a Pyodide worker, with xterm.js for terminal input and output. See that directory for local preview, Playwright testing, and GitHub Pages setup.

Tests live alongside the implementation. The main public embedding coverage is in riz/embedding_test.py, and the asyncio driver is exercised in riz/asyncio_test.py.

Releases

Publishing a GitHub release runs the Python checks, builds a wheel and source distribution, verifies the installed wheel, and publishes those artifacts to PyPI using Trusted Publishing. The release tag must exactly match the version in pyproject.toml, such as 0.1 (without a v prefix).

Before the first release, configure a Trusted Publisher in the PyPI riz project with owner ryanhiebert, repository riz, workflow publish.yml, and environment pypi. Create the matching pypi GitHub environment with Selected branches and tags, one Tag rule with name pattern [0-9]*, no branch rules, and no required reviewers. No PyPI API token is needed.

For each release:

  1. Set the version in pyproject.toml and turn the CHANGELOG.md Unreleased section into a dated version entry, documenting any breaking changes.
  2. Merge the changes and confirm the Build and Browser shell workflows pass.
  3. Create and publish a GitHub release at that commit with the matching version tag and changelog notes.
  4. Confirm the Publish workflow succeeds and the release appears on PyPI.

uv.lock remains ignored so fresh CI runs resolve the latest dependencies allowed by pyproject.toml.

Embedded output

The runtime owns no output stream. Built-in print suspends with the public, immutable riz.OutputRequest(text), whose Python string includes the appended newline. A host writes or collects request.text verbatim and resumes the computation with riz.Ok(riz.Unit()). This works through nested ordinary function calls. Use Runtime.start and the existing advance/resume boundary; synchronous Runtime.evaluate cannot resolve suspensions. CLI and REPL drivers write requests to stdout. The async resolver passed to riz.asyncio.run handles the same request:

import asyncio
import riz
import riz.asyncio

output: list[str] = []

async def resolve(request: object) -> riz.Result[riz.Value]:
    assert isinstance(request, riz.OutputRequest)
    output.append(request.text)
    return riz.Ok(riz.Unit())

started = riz.Runtime().start('print("Hello from an embedded program!")')
assert isinstance(started, riz.Ok)
assert asyncio.run(riz.asyncio.run(started.value, resolve)) == riz.Ok(riz.Unit())
assert output == ["Hello from an embedded program!\n"]

A host may instead resume with riz.Err(error) to fail the native call. The error propagates through Riz execution just like an immediate native failure; Riz does not yet have syntax to catch it. Resolvers return Ok(value) or Err(error). Python exceptions raised by a resolver or console write still remain host exceptions; automatic translation and cancellation semantics are still open questions.

Metadata

Release files for riz 0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for riz 0.1
File Size Uploaded Provenance Actions
riz-0.1.tar.gz 48.5 kB With provenance Details for riz-0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for riz 0.1
File Interpreter ABI Platform Actions
riz-0.1-py3-none-any.whl Python 3 none any Details for riz-0.1-py3-none-any.whl

Total release size: 102.2 kB

Release files / riz-0.1.tar.gz

Download URL riz-0.1.tar.gz
Size 48.5 kB
Tags Source
SHA-256 checksum
How to use checksums
3215069b9affe7401cdad1a3f4ce5dc756554476042cb259f93a5741db309386
BLAKE2b-256 checksum
How to use checksums
d5779e58bfbbee6092d98fcbb7265a76e6fd3b3782dc318b150e6517af56eafd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / riz-0.1-py3-none-any.whl

Download URL riz-0.1-py3-none-any.whl
Size 53.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e98aedae4498ed9aca388a67e1a14e4e014116caaa4be604f44e9b9ac4138785
BLAKE2b-256 checksum
How to use checksums
84dcb841ee8332023a2ef942eb8a47ed22d8b977d0ae616705152553ebbb1ae7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Security for riz 0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for riz-0.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page