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.
OptionandResultvariants support exhaustive matching.- Host modules are typed, lazy, and independent of a filesystem layout.
- Python interoperability is explicit at the foreign boundary.
- A
Runtimecan define values, native functions, and host modules. - A
Computationis a unique, mutable, opaque execution machine. - Computations report three nonterminal or terminal states:
Yielded,Suspended(request), andFinished(result). - Ordinary Riz functions may suspend through nested calls without a distinct
async function type or visible
async/awaitboundary. - Native callbacks return
Ok(value),Err(error), orSuspend(request). - Execution policy belongs outside the runtime. A small
asynciodriver 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
Resultvalues, 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:
- Start from a gap at the current language/runtime frontier, not from generic feature completeness.
- 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.
- Prefer work that proves a semantic or embedding contract shared by Python, Zig, CPython, and MicroPython.
- Favor correctness, static knowledge, immutable defaults, and explicit host boundaries.
- Avoid building Python-specific machinery unless it validates a portable interface or delivers lasting Python interoperability.
- Use small executable examples and tests to expose design consequences.
- 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:
- Set the version in
pyproject.tomland turn theCHANGELOG.mdUnreleased section into a dated version entry, documenting any breaking changes. - Merge the changes and confirm the Build and Browser shell workflows pass.
- Create and publish a GitHub release at that commit with the matching version tag and changelog notes.
- 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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.