|
| 1 | +// SPDX-License-Identifier: CC-BY-SA-4.0 |
| 2 | += Žiz — Design |
| 3 | +:toc: macro |
| 4 | +:icons: font |
| 5 | + |
| 6 | +toc::[] |
| 7 | + |
| 8 | +Status: *pre-alpha design.* Everything here is intent until `ziz0` runs it. |
| 9 | +See link:TRIAGE.adoc[TRIAGE.adoc] for what was deliberately left out, and why. |
| 10 | + |
| 11 | +== One-paragraph summary |
| 12 | + |
| 13 | +Žiz is a unityped, homoiconic, reflexive language. There is one value domain, |
| 14 | +programs are values, and the reader, printer, evaluator and environment are |
| 15 | +ordinary values reachable from user code. Žiz has *no typechecker* and never |
| 16 | +rejects a program for classification reasons. Instead every term carries a |
| 17 | +*Judgement Evidence Graph* (JEG) entry: claims about the term, each linked to |
| 18 | +the evidence for it (author assertion, static inference, runtime observation, |
| 19 | +test outcome). Tooling *reads* the JEG; the runtime *appends* to it. The |
| 20 | +first implementation, `ziz0`, is an interpreter written in Chapel. |
| 21 | + |
| 22 | +== What Žiz is *not* |
| 23 | + |
| 24 | +It is not dialectical. Expressions are not triads. There is no boundary |
| 25 | +operator, no simplicial complex, no sheaf, no "Real". The name is a pun and |
| 26 | +the pun is permitted in documentation and error messages; it has no |
| 27 | +semantics. See TRIAGE.adoc §Finding 1. |
| 28 | + |
| 29 | +== Unityped, precisely |
| 30 | + |
| 31 | +Following Harper: a "dynamically typed" language is a statically typed |
| 32 | +language with exactly one type. Every Žiz term has type `V`. "Untyped" is |
| 33 | +the same fact seen from the syntax side (no annotations, no static |
| 34 | +judgement). Runtime tag dispatch is pattern-matching on the single sum |
| 35 | +`V = nil + bool + int + real + str + sym + pair + … + (V → V)`. The classical |
| 36 | +denotational backdrop is Scott's D∞ (`D ≅ [D → D]`); we cite it and do |
| 37 | +nothing further with it. |
| 38 | + |
| 39 | +== Core value domain |
| 40 | + |
| 41 | +[cols="1,2,2", options="header"] |
| 42 | +|=== |
| 43 | +| Tag | Payload | Notes |
| 44 | + |
| 45 | +| `nil` | — | the empty list and the false-ish sentinel |
| 46 | +| `bool` | true / false | |
| 47 | +| `int` | arbitrary precision | `ziz0` uses Chapel `int(64)` for now |
| 48 | +| `real` | IEEE 754 binary64 | |
| 49 | +| `str` | UTF-8 byte sequence | *content* may be any Unicode; *syntax* is ASCII |
| 50 | +| `sym` | interned name | |
| 51 | +| `pair` | car, cdr | lists are right-nested pairs ending in `nil` |
| 52 | +| `vec` | contiguous sequence | |
| 53 | +| `map` | ordered associative | |
| 54 | +| `fn` | params, body, env | closures |
| 55 | +| `prim` | host procedure | |
| 56 | +| `env` | frame + parent | first-class |
| 57 | +| `node` | tree-sitter node id | anchor into the CST; used by the JEG |
| 58 | +| `claim` | subject, judgement, evidence | a JEG entry, also a value |
| 59 | +|=== |
| 60 | + |
| 61 | +== Homoiconicity |
| 62 | + |
| 63 | +Source text →(reader)→ `V` →(evaluator)→ `V` →(printer)→ source text. |
| 64 | +The reader's output is the AST; the AST is a list. Macros are ordinary |
| 65 | +functions from `V` to `V` marked with `defmacro`. |
| 66 | + |
| 67 | +== Metaiconicity |
| 68 | + |
| 69 | +*Owner's term; this is the working definition pending the owner's own.* |
| 70 | + |
| 71 | +The mapping between text and values is itself a value. Concretely: |
| 72 | + |
| 73 | +* `(reader)` returns the current reader as a `map` of dispatch entries |
| 74 | + (`char -> fn`). `(set-reader! m)` installs a new one. Reader macros are |
| 75 | + therefore user code, and the *shape* of the language is data. |
| 76 | +* `(printer)` / `(set-printer! m)` likewise. |
| 77 | +* Both are scoped to the current `env`, so a module can change its own |
| 78 | + surface syntax without changing anyone else's. |
| 79 | + |
| 80 | +This is what makes the off-side surface and the plain S-expression surface |
| 81 | +*the same language* with two reader tables, rather than two dialects. It is |
| 82 | +also where a one-glyph alias for the evaluator (e.g. a Cyrillic letter) would |
| 83 | +live *if* the owner wants one: as a reader-table entry mapping to an ASCII |
| 84 | +name, never as a lexical primitive. |
| 85 | + |
| 86 | +== Reflexivity |
| 87 | + |
| 88 | +* `(eval v [env])`, `(current-env)`, `(env-parent e)`, `(env-bindings e)`. |
| 89 | +* `(jeg)` returns the live Judgement Evidence Graph for the current program. |
| 90 | +* `(claim subject judgement . evidence)` appends to it. |
| 91 | +* `(node-of v)` returns the CST anchor of a value if it came from source. |
| 92 | + |
| 93 | +Closest existing relative: Kernel (Shutt) — operatives receive unevaluated |
| 94 | +operands and the caller's environment as first-class objects. Žiz's |
| 95 | +`defmacro` + first-class `env` is a conservative version of that; whether to |
| 96 | +go the full fexpr route is an open question. |
| 97 | + |
| 98 | +== Lexical rules (ASCII-only) |
| 99 | + |
| 100 | +The surface syntax uses *only printable ASCII* plus newline and tab. |
| 101 | +Identifiers may not contain non-ASCII. String *contents* may. Rationale: |
| 102 | +grep/diff/review/terminal/keyboard hygiene, and because anything non-ASCII |
| 103 | +that matters can be introduced as a reader alias (§Metaiconicity). |
| 104 | + |
| 105 | +=== `sexp` reader |
| 106 | + |
| 107 | +---- |
| 108 | +( ) [ ] { } ' ` , ,@ ; <comment to EOL> |
| 109 | +"string with \" \\ \n escapes" |
| 110 | +integer ::= -?[0-9]+ |
| 111 | +real ::= -?[0-9]+\.[0-9]+([eE][-+]?[0-9]+)? |
| 112 | +symbol ::= [A-Za-z_+\-*/<>=!?%&|^~$][A-Za-z0-9_+\-*/<>=!?%&|^~$.:]* |
| 113 | +keyword ::= :symbol |
| 114 | +---- |
| 115 | + |
| 116 | +`[ ]` reads as a `vec`, `{ }` as a `map`. |
| 117 | + |
| 118 | +=== `layout` reader (off-side) |
| 119 | + |
| 120 | +A line with two or more forms is an implicit list. A following block that is |
| 121 | +indented deeper continues that list, one form (or nested implicit list) per |
| 122 | +line. A line with a single form is *not* wrapped. `\` at end of line |
| 123 | +continues. Explicit brackets disable layout inside them. |
| 124 | + |
| 125 | +Implemented as a tree-sitter *external scanner* emitting |
| 126 | +`INDENT` / `DEDENT` / `NEWLINE` from an indent stack — off-side syntax is not |
| 127 | +expressible in a context-free grammar, which is why any "N-rule EBNF" claim |
| 128 | +for a layout language is wrong on its face. |
| 129 | + |
| 130 | +---- |
| 131 | +define (fact n) |
| 132 | + if (= n 0) |
| 133 | + 1 |
| 134 | + * n (fact (- n 1)) |
| 135 | +---- |
| 136 | + |
| 137 | +reads identically to |
| 138 | + |
| 139 | +---- |
| 140 | +(define (fact n) (if (= n 0) 1 (* n (fact (- n 1))))) |
| 141 | +---- |
| 142 | + |
| 143 | +== Special forms |
| 144 | + |
| 145 | +`quote` `quasiquote` `unquote` `unquote-splicing` `if` `define` `set!` |
| 146 | +`lambda` `defmacro` `begin` `let` `claim`. Everything else is a function. |
| 147 | + |
| 148 | +== Evaluation |
| 149 | + |
| 150 | +Eager, left-to-right, lexically scoped, proper tail calls required of any |
| 151 | +conforming implementation (`ziz0` does not have them yet — that fact is a |
| 152 | +JEG entry on `ziz0`, not a lie in this document). |
| 153 | + |
| 154 | +Errors are values. A failed operation returns an `error` map and *records a |
| 155 | +claim* (`(claim node :raised {...} :evidence :runtime)`). Nothing unwinds |
| 156 | +unless the caller asks via `(raise!)`. |
| 157 | + |
| 158 | +== Judgement Evidence Graph |
| 159 | + |
| 160 | +Full model in link:docs/JEG.adoc[docs/JEG.adoc]. Summary: |
| 161 | + |
| 162 | +* *Judgement*: a proposition about a subject (`:callable`, `:arity 2`, |
| 163 | + `:returns :int`, `:pure`, `:raised`, `:tested-by`, …). Open vocabulary. |
| 164 | +* *Evidence*: why we believe it — *kind* (`:asserted`, `:inferred`, |
| 165 | + `:observed`, `:tested`, `:contradicted`) and *provenance* (who/what/when). |
| 166 | +* Subjects are CST node ids; the graph survives re-parsing via tree-sitter's |
| 167 | + incremental edit tracking. |
| 168 | +* Append-only during a run; merged across runs. |
| 169 | + |
| 170 | +A JEG lint reports *unsupported* and *contested* judgements. It never blocks |
| 171 | +a build; a `Mustfile` may make a threshold a gate — project policy, not |
| 172 | +language semantics. |
| 173 | + |
| 174 | +== Toolchain |
| 175 | + |
| 176 | +Names are the owner's: `claudia`, `boggs`, `federici`, `dunayevskaya`, |
| 177 | +`assata`. *Role assignment is undecided.* Two candidate mappings exist |
| 178 | +(this repo's original guess, and Gemini's); the owner picks. Needed roles: |
| 179 | + |
| 180 | +. reader/printer registry (metaiconic tables) |
| 181 | +. evaluator core |
| 182 | +. JEG store / merge / query |
| 183 | +. grammar, CST anchoring, editor integration |
| 184 | +. CLI / REPL / project driver |
| 185 | +. (later) memory / resource management, if not delegated to Chapel |
| 186 | + |
| 187 | +== Bootstrap plan |
| 188 | + |
| 189 | +. `ziz0` (Chapel): `sexp` reader, evaluator, printer, in-memory JEG. |
| 190 | + Single locale. No FPGA. |
| 191 | +. `ziz0` gains the `layout` reader via the tree-sitter C parser through |
| 192 | + Chapel's C interop. |
| 193 | +. JEG persisted (`.jeg.a2ml`, see JEG.adoc). |
| 194 | +. Self-hosting: reader/evaluator rewritten in Žiz, run under `ziz0`. A |
| 195 | + λ-calculus interpreter with the Y combinator is the smoke test. |
| 196 | +. Only then: backends. Chapel-PGAS distribution via `chapeliser` first, |
| 197 | + because the host is already Chapel. FPGA acceleration lives inside |
| 198 | + `chapeliser`, behind Chapel; Žiz never sees it. |
| 199 | + |
| 200 | +== Related work (real) |
| 201 | + |
| 202 | +Kernel (Shutt, fexprs + first-class envs) · Refal (structural rewriting) · |
| 203 | +Scheme R7RS-small (the sexp core) · sweet-expressions / SRFI-110 (layout |
| 204 | +over S-expressions) · Harper, PFPL ch. 22 (unityped) · Scott 1969 (D∞). |
| 205 | + |
| 206 | +== Non-goals for the foreseeable future |
| 207 | + |
| 208 | +Static types. A typechecker under another name. Triads. Kaomoji. Bitstreams. |
0 commit comments