journal: pin human-readable prose surface (family 20)
This commit is contained in:
+138
@@ -8741,3 +8741,141 @@ build + test green.
|
||||
common enough to warrant the schema field.
|
||||
- Precise sub-binder analysis for the lint: waits for evidence
|
||||
that the conservative cut misses real cases.
|
||||
|
||||
## 2026-05-08 — Pinned: human-readable prose surface (Family 20 candidate)
|
||||
|
||||
User feature-request, pinned for autonomous design pass:
|
||||
|
||||
> *"AILang ist für Menschen tatsächlich ziemlich unleserlich. Ich
|
||||
> hätte gern einen Menschen-lesbaren Text-Output, der semantisch
|
||||
> exakt identisch zu ail ist, aber von Menschen gut gelesen werden
|
||||
> kann. Dabei sind Formatierung, Inlining, klare Identifier (keine
|
||||
> Klammerhölle!), vllt. Infix-Notation, etc. wichtig. Und eventuell
|
||||
> das Weglassen von Spezifika, die nur der Sprachstringenz dienen.
|
||||
> Der Zweck ist: 1. Menschen sollen den Code schneller begreifen.
|
||||
> 2. Sie sollen den Code durch FREITEXT editieren können. Der
|
||||
> editierte Code und der Original-Code gehen dann wieder an das
|
||||
> LLM, das dann in AIL die Freitext-Ergänzungen einbaut. Damit
|
||||
> beginnt der Zyklus von vorne."*
|
||||
|
||||
### Read of the request
|
||||
|
||||
Two artefacts are wanted:
|
||||
|
||||
1. **A renderer.** `Module → human-prose String`. Indented,
|
||||
infix-flavoured, parens dropped where unambiguous, Lispy
|
||||
`(con T)` / `(term-ctor ...)` machinery suppressed in favour
|
||||
of conventional notation (`T`, `Cons(1, Nil)`).
|
||||
2. **A round-trip mediator.** Original .ail.json + edited prose
|
||||
→ updated .ail.json. The mediator is the LLM, not the
|
||||
compiler. Renderer is deterministic; round-trip is not (LLM
|
||||
interprets free-text intent).
|
||||
|
||||
The user's word "semantisch exakt identisch" applies to the
|
||||
**renderer's output as a projection of the source**: re-reading
|
||||
the prose alongside the .ail.json gives a human all the semantic
|
||||
information needed to reason about the program. The user's
|
||||
"Weglassen von Spezifika, die nur der Sprachstringenz dienen"
|
||||
explicitly authorises *lossy* projection — the prose may omit
|
||||
machinery the LLM can re-derive (e.g. `consume_count` is already
|
||||
inferred, so the prose definitely doesn't show it; redundant
|
||||
parens can go; `(con Int)` becomes `Int`). The full .ail.json
|
||||
remains the canonical source.
|
||||
|
||||
### What MUST stay visible in prose
|
||||
|
||||
Anything semantically load-bearing that the LLM cannot trivially
|
||||
re-derive from prose alone:
|
||||
|
||||
- `(own T)` / `(borrow T)` mode annotations on fn signatures —
|
||||
these are contracts (Decision 10), not buchhaltung. Render as
|
||||
`own T` / `borrow T` or a sigil (`&T` / `~T`?). To be decided.
|
||||
- Effect annotations (`IO`, `Diverge`) on user fns.
|
||||
- Explicit `clone` calls — they're an author commitment, not a
|
||||
free choice.
|
||||
- Doc strings.
|
||||
|
||||
### What MAY go
|
||||
|
||||
- Outer `(module …)` wrap (filename or top-line is enough).
|
||||
- `(con T)` wrapping around base types — render as `T`.
|
||||
- `(term-ctor T C f1 f2)` — render as `C(f1, f2)` (or just
|
||||
`Nil` for nullary).
|
||||
- Redundant parentheses around expressions whose precedence is
|
||||
unambiguous.
|
||||
- Fully explicit `(fn-type (params ...) (ret ...))` —
|
||||
render as `(p1: T1, p2: T2) -> R`.
|
||||
- `(let x v body)` — render as `let x = v` (newline) `body` or
|
||||
`x := v; body`.
|
||||
- Non-essential schema-rigour fields (e.g. `consume_count` if
|
||||
the AST ever exposed it; today it's already inferred so this
|
||||
is a forward-proofing note).
|
||||
- The implicit `(do io/print_int x)` ceremony when `print` would
|
||||
do.
|
||||
|
||||
### Round-trip cycle
|
||||
|
||||
For the editor cycle:
|
||||
|
||||
```
|
||||
.ail.json ──[render]──→ .prose.txt
|
||||
↓ user edits freely
|
||||
.prose.txt' (edited)
|
||||
↓
|
||||
.ail.json + .prose.txt' ──[LLM mediator]──→ .ail.json'
|
||||
```
|
||||
|
||||
The LLM mediator is the contract-enforcer here: it integrates
|
||||
free-text edits while preserving annotations the prose may have
|
||||
omitted (e.g. carries forward the original `(own T)` annotation
|
||||
unless the user's edit obviously contradicts it). This is *not*
|
||||
a compiler pass — the renderer ships first, the mediator is
|
||||
a separate piece (probably a `prompt_template` + a calling
|
||||
convention, not a Rust crate).
|
||||
|
||||
### Iter scope
|
||||
|
||||
Probably a small family. First cut design:
|
||||
|
||||
- **Iter 20a — renderer skeleton.** New crate `ailang-prose`
|
||||
with `pub fn module_to_prose(&Module) -> String`. Covers the
|
||||
full AST with a conservative formatting policy (no infix yet,
|
||||
no inlining, just kill the `(con ...)` / `(term-ctor ...)`
|
||||
noise and use proper indentation + commas). One snapshot test
|
||||
per fixture in `examples/`.
|
||||
|
||||
- **Iter 20b — formatting polish.** Infix for arithmetic
|
||||
(`(i64-add a b)` → `a + b`), drop redundant parens, inline
|
||||
short let-bindings, prettier match arms. Snapshot tests
|
||||
update; behavioural surface unchanged.
|
||||
|
||||
- **Iter 20c — `ail prose` CLI subcommand.** `ail prose <file.ail.json>`
|
||||
prints the prose to stdout. Symmetric to `ail parse`'s role.
|
||||
|
||||
- **Iter 20d — round-trip ergonomics.** This is where the LLM
|
||||
mediator's input format gets specified. May be a documented
|
||||
prompt template under `docs/`, may grow into a `ail merge-prose`
|
||||
subcommand that wraps an LLM call. Defer until 20a–c are real
|
||||
code we're using.
|
||||
|
||||
### Why this is its own family
|
||||
|
||||
Decision 10 / Family 18 was the memory model. Family 20 is a
|
||||
**presentation layer** — it touches no semantics, no codegen, no
|
||||
typechecker. Lives entirely above `ailang-core` as a sibling of
|
||||
`ailang-surface` (which is the form-A *parser* / printer; prose
|
||||
is the form-B *projection*).
|
||||
|
||||
### What's deferred / open questions for design pass
|
||||
|
||||
- How to render `(own T)` / `(borrow T)` in prose. Sigil vs.
|
||||
keyword. Sigils are denser; keywords align with the
|
||||
explicit-intent philosophy. Lean one way and document
|
||||
rationale.
|
||||
- Module headers / imports — render or suppress?
|
||||
- Whether prose carries hash/version annotations for the
|
||||
round-trip (so the mediator can detect drift between prose
|
||||
and the .ail.json the user *thinks* they're editing).
|
||||
- Whether snapshot tests live in `crates/ailang-prose/tests/`
|
||||
or `examples/` (as `.prose.txt` siblings to the .ail.json).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user