Purity Errors
tyc::impure_pure_fn
A function marked @pure violates one of the six purity conditions:
- Synchronous. No coroutines or generators.
- Hashable parameters.
- No I/O in the transitive call graph.
- No non-determinism (no
time.*,random.*,uuid.*,os.urandom). - No reads/writes of mutable module-level state.
- No exceptions raised.
Example:
@puredef fetch(url: str) -> str: import urllib.request return urllib.request.urlopen(url).read().decode() # ❌ I/Oerror[tyc::impure_pure_fn]: `fetch` is annotated `@pure` but performs I/O (urllib.request.urlopen) ┌─ src/main.ty:3:12 │3 │ return urllib.request.urlopen(url).read().decode() │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ I/O is not allowed in `@pure` functionsFix: drop @pure (and lose memoisation eligibility), or refactor to take the I/O result as a parameter.
What’s I/O?
open(...),socket,subprocess,urllib,requests,httpx,aiohttp.print(), logger calls (logger.warning(...)on any logger-named receiver).- Database drivers.
- Filesystem operations, and I/O method names on any receiver (
.read_text(),.write(),.send(),.sleep(),.execute()). unsafe:calls (unless the stub is annotated@pure).
Calls are resolved through the module’s import aliases, so datetime.now() after from datetime import datetime and np.random.rand() after import numpy as np are seen for what they are. A call the checker cannot classify — a method on a value of unknown type, a module outside the pure stdlib allow-list — is trusted under @pure; only the automatic optimisations insist on provable purity.
What’s non-determinism?
time.time(),time.monotonic(),datetime.now().random.*,secrets.*.uuid.uuid4()(the others —uuid1,uuid3,uuid5— are deterministic if their inputs are).os.urandom().
What’s mutable module state?
- Reads from a module-level
mutbinding (or one rebound after its definition, or declaredglobalin some function). - Writes to any module-level binding (including
let, via mutation through fields or a mutating method such asREGISTRY.append(x)). - Writes to an argument —
c.n = c.n + 1,xs.pop(),next(it)on a parameter — are impure for the same reason: the caller can see them.
comptime let reads are fine — those are inlined as literals.
Where next
@pureand@memoreference — the six conditions in detail.- Advanced Features (tour) — teaching page.