https://github.com/hyperpolymath/affinescript
AffineScript — affine type system programming language
https://github.com/hyperpolymath/affinescript
hyperpolymath palimpsest
Last synced: about 1 month ago
JSON representation
AffineScript — affine type system programming language
- Host: GitHub
- URL: https://github.com/hyperpolymath/affinescript
- Owner: hyperpolymath
- License: other
- Created: 2026-03-21T01:23:33.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-04-10T22:33:49.000Z (about 1 month ago)
- Last Synced: 2026-04-10T23:20:23.428Z (about 1 month ago)
- Topics: hyperpolymath, palimpsest
- Language: OCaml
- Size: 1 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.adoc
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY-SETUP.md
- Governance: docs/governance/CODE_OF_CONDUCT.md
- Roadmap: ROADMAP.adoc
Awesome Lists containing this project
README
= AffineScript: A Practical Language for Resource-Safe Systems
:toc:
:toclevels: 3
:sectnums:
:source-highlighter: rouge
:icons: font
License: PMPL-1.0-or-later +
Built alongside Gossamer, typed-wasm, and Burble
Write software where the compiler helps enforce resource lifecycles, protocol states, and effect boundaries before bugs become runtime failures.
[NOTE]
====
Honest status sync (2026-04-12): affine/QTT and borrow checking are wired into the standard CLI paths today (`check`, `compile`, `eval`) and gate user programs. Dependent/refinement features are still parse-first in places, and some backend features remain incomplete, especially around effect-handler lowering in Wasm targets. See `.machine_readable/6a2/STATE.a2ml` for the authoritative per-feature status.
====
== What AffineScript Is
AffineScript is a practical programming language for building software where the hard problems are not just “does it parse?” or “does it type-check?”, but:
- is this resource used correctly?
- are these state transitions valid?
- are effects explicit?
- is this extension boundary safe?
- can this code be portable across runtimes and targets?
It combines:
- affine and quantitative typing for ownership and usage tracking
- algebraic effects for explicit side effects and handlers
- row polymorphism for extensible records and interfaces
- refinement-oriented type structure for stronger invariants
- WebAssembly-oriented deployment paths for portable execution
The goal is not to turn ordinary programming into theorem proving. The goal is to make it easier to write programs whose _resource lifecycles_, _protocol states_, and _effect boundaries_ are correct by construction.
AffineScript is not a “game language.” Games have been one useful proving ground for it, but the language is intended more broadly for:
- plugin systems
- desktop/runtime shells
- protocol-driven software
- real-time systems
- data and document pipelines
- simulation and game logic
- extensible applications and toolchains
== The Core Idea
Many bugs in real software are really _type-system omissions in disguise_.
Examples:
- using a resource after it should have been consumed
- forgetting to release a resource
- calling an operation in the wrong protocol state
- hiding I/O or mutation inside supposedly pure logic
- making “extensible” interfaces that are really unchecked convention
- evolving data structures in ways that quietly break consumers
AffineScript moves these constraints into the language.
It is designed for code where:
- resources matter
- state matters
- sequencing matters
- extensibility matters
- portability matters
== The Important Distinction: Core Language vs Faces
AffineScript is the semantic core.
On top of that core, the language supports _faces_: sugared surface syntaxes that let people approach the same semantics through different aesthetic and ergonomic styles.
A face is not a separate language. It is a different presentation layer over the same core model.
Examples include:
- *AffineScript* — the canonical face
- *JaffaScript* — a JavaScript-like face
- *RattleScript* — a Python-like face
- *PseudoScript* — a pseudocode-oriented face
This means people can bring familiarity from a language family they already love, while still entering a system with stronger guarantees around ownership, effects, state, and resource usage.
In other words:
[quote]
____
Different faces, same cube.
____
The syntax may feel familiar in different ways, but the semantic guarantees come from the same checked core.
== Why Faces Matter
Most programming languages force a false choice:
- familiar syntax with weak guarantees, or
- strong guarantees with alien ergonomics
AffineScript’s face system is meant to break that tradeoff.
With faces, you can let users write in a style that feels:
- JavaScript-like
- Python-like
- pseudocode-like
- domain-specific
- pedagogy-friendly
while still preserving:
- type checking
- ownership/usage constraints
- effect tracking
- state and protocol correctness
- portable code generation targets
This is especially useful for:
- onboarding
- teaching
- domain experts entering safer systems programming
- plugin authors
- teams migrating from dynamic or weakly typed languages
== What Makes AffineScript Distinctive
AffineScript brings together capabilities that are rarely available in one practical system:
[cols="1,3"]
|===
|Capability |What it is for
|Affine / QTT-style usage tracking
|Ownership, resource protocols, “use once / don’t duplicate / don’t forget”
|Algebraic effects
|Explicit side effects, handler-based composition, safer impurity boundaries
|Row polymorphism
|Extensible records and interfaces without collapsing into ad hoc objects
|Refinement-oriented typing
|Stronger invariants over state and structure
|Portable code generation
|Wasm-oriented deployment and integration paths
|Multiple faces
|Different surface syntaxes over the same checked semantic core
|===
The practical result is a language aimed at software where correctness depends on more than ordinary static typing.
== What AffineScript Helps Express and Verify
AffineScript is especially suited to programs that need guarantees around:
- resource acquisition and disposal
- protocol state transitions
- plugin and extension boundaries
- explicit capability/effect boundaries
- extensible structured data
- portable execution targets
- interpreters, runtimes, and shells
- host/guest integration layers
This includes code such as:
- network/session flows
- document or file pipelines
- desktop shell logic
- stateful embedded runtimes
- simulation/game systems
- tool plugins
- language tooling and automation
== Example: Resource-Safe Ownership
[source,affine]
----
type Texture = own { id: Int, width: Int, height: Int }
fn load_texture(path: ref String) -{IO + Exn[LoadError]}-> own Texture {
Texture { id: 42, width: 1024, height: 1024 }
}
fn render(scene: ref Scene, texture: ref Texture) -{Render}-> () {
()
}
fn unload(@linear texture: own Texture) -{IO}-> () {
()
}
fn frame() -{IO + Render + Exn[LoadError]}-> () {
let texture = load_texture("player.png");
render(scene, texture);
unload(texture);
// texture cannot be used here
}
----
What the checker is trying to protect you from:
- using a consumed resource
- forgetting to release a resource
- duplicating exclusive ownership
- hiding lifecycle mistakes behind convention
== Example: State-Checked Protocol Transitions
[source,affine]
----
type Connection[..state] = own {
socket: own Socket,
..state
}
fn authenticate(
@linear conn: own Connection[{status: Unauthenticated}]
) -{Session + IO}-> Connection[{status: Authenticated, user: String}] {
let creds = recv();
Connection { socket: conn.socket, status: Authenticated, user: creds.user }
}
fn query(
conn: ref Connection[{status: Authenticated, ..r}],
sql: ref String
) -{IO}-> Result[Rows, DbError] {
// ...
}
----
What the checker can rule out:
- querying before authentication
- authenticating twice through the same consumed handle
- using a connection after close
- silently invalid protocol transitions
== Example: Effect-Explicit Code
[source,affine]
----
effect IO {
fn print(s: String) -> ();
fn read_line() -> String;
}
effect State[S] {
fn get() -> S;
fn put(s: S) -> ();
}
fn interactive_counter() -{IO + State[Int]}-> Int {
let input = read_line();
let current = get();
let next = current + 1;
put(next);
print("Count: " ++ int_to_string(next));
next
}
fn add(a: Int, b: Int) -> Int {
a + b
}
----
The point is not “effects are fancy.” The point is that effect boundaries become visible and checkable.
== Example: Extensible Data with Row Polymorphism
[source,affine]
----
fn greet[..r](person: {name: String, ..r}) -> String {
"Hello, " ++ person.name
}
let alice = {name: "Alice", age: 30, role: "Engineer"};
let bob = {name: "Bob", department: "Sales"};
greet(alice);
greet(bob);
----
This matters for extensible systems, plugins, records, evolving schemas, and interface stability.
== Faces in More Detail
A face is a syntax layer, not a semantic fork.
That means:
- the same ownership rules still apply
- the same effect rules still apply
- the same state/protocol guarantees still apply
- the same backend targets still apply
The purpose of faces is not fragmentation. It is _approachability_.
A likely way to think about the stack is:
[listing]
----
face syntax -> AffineScript core AST -> type/effect/ownership checks -> backend target
----
Examples:
- JaffaScript lowers JavaScript-like syntax into the AffineScript core
- RattleScript lowers Python-like syntax into the same core
- PseudoScript lowers structured pedagogical pseudocode into the same core
So the question is not “which face is the real language?”
The answer is: the core semantics are the language; faces are entrances.
== Backends and Targets
AffineScript is intended to support portable deployment, especially through WebAssembly-oriented targets.
Depending on feature maturity and backend coverage, this includes:
- Wasm-oriented deployment paths
- integration with typed-wasm conventions
- native/runtime-specific backends where implemented
- host integration via Gossamer and related infrastructure
The important point is this:
[quote]
____
AffineScript is not “JavaScript with types” and not “Python with ownership syntax.”
It is a checked core language that can be surfaced through multiple familiar faces and lowered into portable execution targets.
____
== Relationship to typed-wasm
AffineScript and typed-wasm are related, but they are not the same thing.
- *AffineScript* is a source language and semantic system.
- *typed-wasm* is about typed structure and safety around Wasm-oriented memory/layout/interface conventions.
- *Gossamer* is a resource-safe host/runtime/shell layer.
- *Burble* is a high-assurance real-time systems application of the same broader philosophy.
A helpful mental model is:
[listing]
----
face syntax -> AffineScript core -> typed-wasm / Wasm conventions -> host/runtime integration
----
typed-wasm matters because portable low-level targets need more than “it compiles.” They need conventions, structure, and safe composition across boundaries.
== Games Are a Proving Ground, Not the Definition
Games have been a useful proving ground because they combine:
- stateful logic
- resources with lifecycles
- extensible entity data
- eventful and asynchronous behaviour
- portability requirements
- host/runtime integration concerns
That makes them an excellent stress test.
But AffineScript is not limited to games, and should not be presented as if that were its sole or primary identity.
Games are one place where the language’s properties are easy to demonstrate. They are not the language’s entire reason for existing.
== Getting Started
=== Prerequisites
- OCaml 5.1+
- Dune 3.14+
- opam packages: `sedlex`, `menhir`, `ppx_deriving`, `cmdliner`, `yojson`
=== Build
[source,bash]
----
dune build
----
=== Type check a file
[source,bash]
----
_build/default/bin/main.exe check test/e2e/fixtures/affine_basic.affine
----
=== Evaluate with interpreter
[source,bash]
----
_build/default/bin/main.exe eval test/e2e/fixtures/interp_simple.affine
----
=== Compile
[source,bash]
----
_build/default/bin/main.exe compile -o hello.wasm test/e2e/fixtures/wasm_simple.affine
----
=== Format
[source,bash]
----
_build/default/bin/main.exe fmt test/e2e/fixtures/affine_basic.affine
----
=== Lint
[source,bash]
----
_build/default/bin/main.exe lint test/e2e/fixtures/affine_basic.affine
----
=== JSON diagnostics
[source,bash]
----
_build/default/bin/main.exe check --json test/e2e/fixtures/affine_basic.affine
----
[NOTE]
====
The standard source extension for AffineScript is `.affine`.
====
== Status
AffineScript is in active development.
This repository contains a live compiler and tooling stack, but some advanced features remain incomplete or partial. Current state is best understood from the machine-readable status files and current docs rather than from aspirational claims.
[cols="2,1,4"]
|===
|Component |Status |Notes
|Lexer + Parser
|Complete
|Menhir grammar, sedlex tokenizer, broad syntax coverage
|Name Resolution
|Complete
|Module loading, scoping, imports
|Type Checker
|Wired
|Active in standard CLI paths
|Quantity Checking
|Live gate
|QTT quantity checks fail builds on violations
|Borrow Checker
|Live gate
|Compile-time borrow checks active; advanced phases ongoing
|Effect System
|Interpreter-complete
|Handlers/interpreter path active; backend parity incomplete
|Trait System
|Partial
|Core registry and lookup work exists; advanced coherence remains
|Interpreter
|High
|Closures, matching, effects, and recent exception plumbing
|WASM Codegen
|Advanced but incomplete
|Primary deployment backend; feature gaps remain
|WASM GC Codegen
|Partial
|Available, not full feature parity
|LSP Server
|Complete
|Hover, goto-def, completion, diagnostics
|Formatter + Linter
|Complete
|AST-based formatter and lint rules
|===
== Design Principles
- *Correctness should be structural, not ceremonial.*
- *Types should clarify what code may do, not merely classify data.*
- *Effects should be explicit.*
- *Resources should not depend on discipline alone.*
- *Extensibility should not collapse into unchecked runtime convention.*
- *Strong guarantees should not require alien syntax.*
- *Different faces should be able to share one checked core.*
== Repository Structure
[listing]
----
affinescript/
+-- lib/ # Core compiler (OCaml)
| +-- ast.ml
| +-- lexer.ml
| +-- parser.mly
| +-- typecheck.ml
| +-- unify.ml
| +-- quantity.ml
| +-- types.ml
| +-- codegen.ml
| +-- interp.ml
| +-- json_output.ml
+-- bin/main.ml # CLI
+-- tools/affinescript-lsp/ # Language server
+-- editors/ # Editor integrations
+-- stdlib/ # Standard library modules
+-- examples/ # Example programs
+-- test/ # Golden tests and fixtures
+-- docs/ # Specifications, guides, papers
+-- .machine_readable/ # Authoritative machine-readable status
----
== Ecosystem
AffineScript sits inside a broader ecosystem built around correctness-by-construction.
=== Gossamer
A resource-safe host/runtime shell for desktop and embedded application structures.
=== typed-wasm
A typed Wasm-oriented layer for memory/layout/interface discipline and safer low-level convergence.
=== Burble
A high-assurance real-time communications system using similar design principles around correctness, performance, and explicit structure.
These are related projects, but each has a distinct role.
== What AffineScript Is Not
AffineScript is not:
- merely “Rust with different syntax”
- merely “TypeScript but safer”
- merely “a game scripting language”
- merely “a proof assistant disguised as a language”
- merely “a Wasm frontend”
It is better understood as:
[quote]
____
A practical language core for resource-aware, stateful, effect-explicit, extensible software — with multiple faces and portable targets.
____
== Documentation
- `docs/` — language and architecture documentation
- `.machine_readable/6a2/STATE.a2ml` — authoritative feature status
- `examples/` — example programs
- `tools/affinescript-lsp/` — language tooling
- `editors/` — editor integrations
== License
This repository is documented and distributed under AGPL-3.0-or-later.
The broader Palimpsest-MPL licensing layer applies to associated ecosystem technology including:
- Gossamer
- Burble
- AffineScript deployment/tooling layers where applicable
See also:
- AGPL-3.0-or-later: https://www.gnu.org/licenses/agpl-3.0.html
- PMPL-1.0: https://github.com/hyperpolymath/palimpsest-license
== Final Positioning Summary
AffineScript should be presented as:
[quote]
____
A practical checked core language for resource-safe, stateful, extensible software, with multiple familiar faces and portable Wasm-oriented targets.
____
Not as:
[quote]
____
A secret weapon for games.
____