Web apps whose compiler refuses whole classes of bugs.
Pleris is a language for web apps, with a compiler and a server for it. You write the data, the server's reads and writes, and the pages in one language, and the compiler refuses programs with bugs that TypeScript, React, Next.js and Svelte let through to production.
module price
fn price(cents: Int) -> String !{} {
cents
}error: [PW0606] `price.price` declares its result `String` and this produces `Int`
--> price.pw:4:5
|
3 | fn price(cents: Int) -> String !{} {
| ----- `price` declares its result here
4 | cents
| ^^^^^ `price.price` declares its result `String` and this produces `Int`
|
= note: every value a body returns — its last expression, each branch of it, and each `return` — must have the result type its signature declares
= help: produce a `String` here, or change the declared resultWhat the compiler refuses
Each refusal has a code, a one-sentence rule and the decision behind it: 220 codes, from the compiler's own registry.
A user's data in a cache everyone shares
A query says whose data it returns, and a session's data cannot sit in a shared cache.
PW5001Rendering that reaches the database
A view declares that it has no effects, and the compiler checks the claim through every call it makes.
PW0401A function that says it is pure and is not
An effect row is a claim: a helper that reads the clock makes every caller that claims purity wrong.
PW0400A case forgotten
Every match covers every case, and a misspelt case is a case the type lacks, never a catch-all.
PW0305A link to nowhere
Every internal link is matched against the program's routes.
PW5009A form control nobody can name
A control a screen reader cannot announce is refused, as are a duplicate id and a page with no title.
PW5014
Every error code · What it refuses, and what it does not do yet
Fast where a reader feels it
The same apps in Pleris, React, Svelte, Next.js and SvelteKit, measured by the project's own instrument: Chromium on one development PC, medians of five runs; Slow 4G is Lighthouse's profile of a slow phone network.
| Measured | Pleris | React or Next.js | Svelte or SvelteKit |
|---|---|---|---|
| TodoMVC: the first todo added, to the frame that shows it (Slow 4G) | 31 ms | 33 ms React | 34 ms Svelte |
| A store: ready to use on a first visit (Slow 4G) | 1,381 ms | 2,067 ms Next.js | 1,749 ms SvelteKit |
| A store: ready to use on the next visit (Slow 4G) | 626 ms | 660 ms Next.js | 638 ms SvelteKit |
| A store: a press with 19 other tabs of it open | 21 ms | 43 ms Next.js | 44 ms SvelteKit |
| A store: server CPU for each request, under load | 0.20 ms | 5.12 ms Next.js | 1.17 ms SvelteKit |
Where it still loses: the first download (TodoMVC sends 142 KB, Svelte 21 KB), Svelte's first visit by about 120 ms, and latency under load against SvelteKit, whose server keeps connections open. These numbers come from a branch still under review (PR #76), and field performance is not claimed. The runs, the commands and every row, losses first.
Every example compiles
Each example in the guide is a whole program, and a test runs every one through the real compiler: one meant to compile must build, one meant to fail must fail with exactly the codes it names, and where a page shows the compiler's output, the compiler must print exactly that.
Build a guestbook with sign-in, end to end.
What it does not do yet
- An app's storage is written in Rust, behind host bindings.
- Sign-in is a development provider, and there is one server.
- No
pw new, no project file and no editor support. - Some checks have gaps, each shown in the guide.