66
77Python-syntax AffineScript.
88Write code that looks like Python.
9- Get affine resource guarantees, algebraic effects, and typed WASM out.
9+ Get affine resource guarantees and typed WASM out.
10+
11+ **Status: alpha** — the syntax, type checker, and WASM output are solid.
12+ Effects runtime is in progress upstream.
1013
1114---
1215
1316== What it is
1417
15- RattleScript is AffineScript with its Python face pre-selected.
16- If you write Python, you already know most of the syntax.
18+ RattleScript is https://github.com/hyperpolymath/affinescript[AffineScript]
19+ with its Python face pre-selected. If you write Python, you already know
20+ most of the syntax.
21+
1722The compiler checks that your resources (files, sockets, tokens) are used
1823*exactly as many times as you say* — and proves it at compile time.
24+ No null pointer exceptions. No use-after-free. No silent data races.
1925
2026[source,python]
2127----
@@ -27,7 +33,7 @@ def main() -> ():
2733 IO.println(msg)
2834----
2935
30- That's a valid ` .rattle` file. Run it :
36+ Save as `hello .rattle` and run :
3137
3238[source,bash]
3339----
@@ -37,106 +43,133 @@ rattle eval hello.rattle
3743== Why Python syntax?
3844
3945Because Python programmers deserve a sound type system.
40- RattleScript is Python's more dangerous cousin — same comfortable whitespace-
41- delimited blocks, but with teeth: no null pointer exceptions, no
42- use-after-free, no data races, and no runtime surprises that the type
43- checker already knows about.
4446
45- The mascot is a rattlesnake. Python's more dangerous cousin.
47+ RattleScript is Python's more dangerous cousin — same comfortable
48+ whitespace-delimited blocks, but with teeth: the compiler tracks *ownership*
49+ of every value so you cannot accidentally drop, duplicate, or outlive a
50+ resource. You never null-check. You never wonder if a function consumes its
51+ argument or borrows it. The type says so.
52+
53+ The mascot is a rattlesnake.
4654
4755== Surface mappings
4856
4957[cols="1,1"]
5058|===
51- |Python surface |AffineScript equivalent
52-
53- |`def f(x: T) -> R:` |`fn f(x: T) -> R { ... }`
54- |`True` / `False` |`true` / `false`
55- |`None` |`()`
56- |`and` / `or` / `not` |`&&` / `\|\|` / `!`
57- |`class Name:` |`type Name`
58- |`pass` |`()`
59- |`import a.b` |`use a::b;`
60- |`from a import b` |`use a::b;`
61- |`if cond:` / `else:` |`if cond { ... } else { ... }`
62- |`for x in e:` |`for x in e { ... }`
63- |`match e:` |`match e { ... }`
64- |`# comment` |`// comment`
59+ |Python surface |What it means in RattleScript
60+
61+ |`def f(x: T) -> R:` |function declaration
62+ |`True` / `False` |`true` / `false`
63+ |`None` |unit `()` — not null, genuinely nothing
64+ |`and` / `or` / `not` |`&&` / `\|\|` / `!`
65+ |`class Name:` |`type Name` (algebraic data type)
66+ |`pass` |`()` (unit expression)
67+ |`import a.b` |`use a::b;`
68+ |`from a import b` |`use a::b;`
69+ |`if cond:` / `else:` |`if cond { ... } else { ... }`
70+ |`elif cond:` |`} else if cond {`
71+ |`for x in e:` |`for x in e { ... }`
72+ |`match e:` |`match e { ... }`
73+ |`# comment` |`// comment`
6574|===
6675
67- See `rattle preview-python-transform <file>` to inspect the full transform
68- for any file.
76+ Run `rattle preview-python-transform <file>` to see the canonical
77+ AffineScript that the preprocessor generates from any `.rattle` file.
78+
79+ == Getting started
80+
81+ === Prerequisites
6982
70- == Installation
83+ * https://ocaml.org/[OCaml] + https://dune.build/[dune] (for the compiler)
84+ * https://www.rust-lang.org/[Rust] + cargo (for the `rattle` wrapper)
85+ * https://just.systems/[just] (task runner)
7186
72- === From source (monorepo dev)
87+ === Install from source
7388
7489[source,bash]
7590----
76- # From nextgen-languages/affinescript root — build affinescript first:
77- dune build
91+ git clone --recurse-submodules https://github.com/hyperpolymath/rattlescript
92+ cd rattlescript
7893
79- # Then build the rattle wrapper:
80- cd distributions/rattlescript
81- just build
82- # Binary at: target/debug/rattle
94+ # Build affinescript compiler + rattle wrapper in one step
95+ just bootstrap
96+
97+ # Optional: install rattle to ~/.cargo/bin
98+ just install
8399----
84100
85- === Installed release
101+ === Usage
86102
87103[source,bash]
88104----
89- just install
90- # Installs rattle to ~/.cargo/bin
91- ----
105+ rattle check hello.rattle # type-check
106+ rattle eval hello.rattle # run
107+ rattle compile hello.rattle # compile to WASM
108+ rattle fmt hello.rattle # format
109+ rattle lint hello.rattle # static analysis
92110
93- === Standalone GitHub repo
111+ # See what the Python preprocessor produces
112+ rattle preview-python-transform hello.rattle
113+ ----
94114
95- The standalone https://github.com/hyperpolymath/rattlescript[hyperpolymath/rattlescript]
96- repo contains a pre-configured distribution with affinescript as a git
97- submodule. Use that for a self-contained checkout.
115+ `.rattle` files are detected automatically — no `--face` flag needed.
116+ You can also use `.pyaff` if you prefer the AffineScript naming.
98117
99- == Usage
118+ == Ownership in one minute
100119
101- [source,bash]
120+ [source,python]
121+ ----
122+ # @linear means: use this exactly once.
123+ def send_token(@linear token: AuthToken) -> Receipt:
124+ network.submit(token) # consumes token — compiler verifies this
125+
126+ # Option instead of None crashes.
127+ def safe_divide(a: Int, b: Int) -> Option[Int]:
128+ if b == 0:
129+ None
130+ else:
131+ Some(a / b)
132+
133+ def show(result: Option[Int]) -> ():
134+ match result:
135+ Some(n) => IO.println(Int.to_string(n))
136+ None => IO.println("no result")
102137----
103- # Type-check a file
104- rattle check hello.rattle
105138
106- # Evaluate
107- rattle eval hello.rattle
139+ The compiler rejects any program that drops a `@linear` value unused,
140+ uses it more than once, or tries to treat `None` as a value.
108141
109- # Compile to WASM
110- rattle compile hello.rattle
142+ == Relationship to AffineScript
111143
112- # See the canonical AffineScript generated by the preprocessor
113- rattle preview-python-transform hello.rattle
144+ RattleScript *is* AffineScript. Same compiler, same type system, same WASM
145+ output. The difference is `--face python` is the default and the entry
146+ point is `rattle` instead of `affinescript`.
114147
115- # All affinescript subcommands work — --face python is pre-injected
116- rattle lint hello.rattle
117- rattle fmt hello.rattle
118- ----
148+ The affinescript compiler lives in the `affinescript/` submodule of this
149+ repo. `just update-affinescript` bumps the pointer to the latest upstream
150+ release — all language improvements flow through automatically.
119151
120- == Relationship to AffineScript
152+ Face logic: `affinescript/lib/python_face.ml` (syntax transform) and
153+ `affinescript/lib/face.ml` (error vocabulary). No compiler internals change
154+ when the face changes.
155+
156+ == Alpha caveats
121157
122- RattleScript *is* AffineScript — same compiler, same type system, same WASM
123- output. The only difference is `--face python` is the default.
158+ * **Effects runtime**: `effect`/`handle` syntax is type-checked but not yet
159+ executed at runtime. The effects story is coming in a near-upstream release.
160+ * **Closures and linear captures**: lambda-body linear capture tracking is
161+ conservative in the current release.
162+ * **Span fidelity**: error locations refer to the transformed canonical source,
163+ not the original `.rattle` line numbers. Source-map remapping is planned.
124164
125- The Python face lives in
126- link:../../lib/python_face.ml[`lib/python_face.ml`] and the error vocabulary
127- in link:../../lib/face.ml[`lib/face.ml`]. No compiler internals change when
128- the face changes.
165+ == Contributing
129166
130- == Sync strategy
167+ This repo is a thin distribution layer. Language bugs and features go
168+ upstream: https://github.com/hyperpolymath/affinescript[hyperpolymath/affinescript].
131169
132- This directory (`distributions/rattlescript/`) lives inside the affinescript
133- monorepo. The standalone https://github.com/hyperpolymath/rattlescript[rattlescript]
134- GitHub repo uses affinescript as a git submodule and contains only:
170+ RattleScript-specific contributions (branding, examples, tutorials, the
171+ `rattle` CLI wrapper) are welcome here.
135172
136- * This `distributions/rattlescript/` content (copied at release time)
137- * Branding assets
138- * CI workflows
173+ == License
139174
140- `just update-affinescript` advances the submodule pointer after each
141- affinescript release. All language changes, face changes, and bug fixes
142- come through that single submodule bump — no duplication.
175+ PMPL-1.0-or-later. See link:LICENSE[LICENSE].
0 commit comments