Hyperscript compiler/runtime:
- query target support in set/fire/put commands
- hs-set-prolog-hook! / hs-prolog-hook / hs-prolog in runtime
- runtime log-capture cleanup
Scripts: sx-loops-up/down, sx-hs-e-up/down, sx-primitives-down
Plans: datalog, elixir, elm, go, koka, minikanren, ocaml, hs-bucket-f,
designs (breakpoint, null-safety, step-limit, tell, cookies, eval,
plugin-system)
lib/prolog/hs-bridge.sx: initial hook-based bridge draft
lib/common-lisp/tests/runtime.sx: CL runtime tests
WASM: regenerate sx_browser.bc.js from updated hs sources
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
132 lines
6.3 KiB
Markdown
132 lines
6.3 KiB
Markdown
# Elm-on-SX: Elm 0.19 on the CEK/VM
|
||
|
||
Compile Elm source to SX AST; the existing CEK evaluator runs it. The unique angle: SX's
|
||
reactive island system (`defisland`, signals, `provide`/`context`) is a natural host for
|
||
The Elm Architecture — Model/Update/View maps almost directly onto SX's reactive runtime.
|
||
This is the only language in the set that targets SX's browser-side reactivity rather than
|
||
the server-side evaluator.
|
||
|
||
End-state goal: **core Elm programs running in the browser via SX islands**, with The Elm
|
||
Architecture wired to SX signals. Not a full Elm compiler — no exhaustiveness checking, no
|
||
module system, no type inference — but a faithful runtime that can run Elm programs written
|
||
in idiomatic style.
|
||
|
||
## Ground rules
|
||
|
||
- **Scope:** only touch `lib/elm/**` and `plans/elm-on-sx.md`. Do **not** edit `spec/`,
|
||
`hosts/`, `shared/`, or other `lib/<lang>/`.
|
||
- **Shared-file issues** go under "Blockers" below with a minimal repro; do not fix here.
|
||
- **SX files:** use `sx-tree` MCP tools only.
|
||
- **Architecture:** Elm source → Elm AST → SX AST. No standalone Elm evaluator.
|
||
- **Type system:** defer. Focus on runtime semantics. Type errors surface at eval time.
|
||
- **Commits:** one feature per commit. Keep `## Progress log` updated and tick boxes.
|
||
|
||
## Architecture sketch
|
||
|
||
```
|
||
Elm source text
|
||
│
|
||
▼
|
||
lib/elm/tokenizer.sx — numbers, strings, idents, operators, indentation-sensitive lexer
|
||
│
|
||
▼
|
||
lib/elm/parser.sx — Elm AST: module, import, type alias, type, let, case, lambda,
|
||
│ if, list/tuple/record literals, pipe operator |>
|
||
▼
|
||
lib/elm/transpile.sx — Elm AST → SX AST
|
||
│
|
||
▼
|
||
lib/elm/runtime.sx — TEA runtime: Program, sandbox, element; Cmd/Sub wrappers;
|
||
│ Html.* shims; Browser.* shims
|
||
▼
|
||
SX island / reactive runtime (browser)
|
||
```
|
||
|
||
Key semantic mappings:
|
||
- `Model` → SX signal (`make-signal`)
|
||
- `update : Msg -> Model -> Model` → SX signal updater (called on each message)
|
||
- `view : Model -> Html Msg` → SX component (re-renders on model signal change)
|
||
- `Cmd` → SX `perform` IO request
|
||
- `Sub` → SX event listener registered via `dom-listen`
|
||
- `Maybe a` → `nil` (Nothing) or value (Just a) — uses ADTs from Phase 6 of primitives
|
||
- `Result a b` → ADT `(Ok val)` / `(Err err)`
|
||
|
||
## Roadmap
|
||
|
||
### Phase 1 — tokenizer + parser
|
||
- [ ] Tokenizer: keywords (`module`, `import`, `type`, `alias`, `let`, `in`, `if`, `then`,
|
||
`else`, `case`, `of`, `port`), indentation tokens (indent/dedent/newline), string
|
||
literals, number literals, operators (`|>`, `>>`, `<<`, `<|`, `++`, `::`), type vars
|
||
- [ ] Parser: module declaration, imports, type aliases, union types, function definitions
|
||
with pattern matching, `let`/`in`, `case`/`of`, `if`/`then`/`else`, lambda `\x -> e`,
|
||
list literals `[1,2,3]`, tuple literals `(a,b)`, record literals `{x=1, y=2}`,
|
||
record update `{ r | x = 1 }`, pipe operator `|>`
|
||
- [ ] Skip for phase 1: ports, subscriptions, effects manager, type annotations
|
||
- [ ] Tests in `lib/elm/tests/parse.sx`
|
||
|
||
### Phase 2 — transpile: expressions + pattern matching
|
||
- [ ] `elm-eval-ast` entry
|
||
- [ ] Arithmetic, string `++`, comparison, boolean ops
|
||
- [ ] Lambda → SX `fn`; function application
|
||
- [ ] `let`/`in` → SX `let`
|
||
- [ ] `if`/`then`/`else` → SX `if`
|
||
- [ ] `case`/`of` with constructor, literal, tuple, list, wildcard patterns → SX `cond`
|
||
using ADT match (Phase 6 primitives)
|
||
- [ ] List ops: `List.map`, `List.filter`, `List.foldl`, `List.foldr`
|
||
- [ ] `Maybe` and `Result` as ADTs
|
||
- [ ] 30+ eval tests in `lib/elm/tests/eval.sx`
|
||
|
||
### Phase 3 — The Elm Architecture runtime
|
||
- [ ] `Browser.sandbox` — pure TEA loop (no Cmds, no Subs)
|
||
`{ init : model, update : msg -> model -> model, view : model -> Html msg }`
|
||
Wires to: SX signal for model, SX component for view, message dispatch on user events
|
||
- [ ] `Html.*` shims: `div`, `p`, `button`, `input`, `text`, `h1`–`h6`, `ul`, `li`, `a`,
|
||
`span`, `img` — emit SX component calls
|
||
- [ ] `Html.Attributes.*`: `class`, `id`, `href`, `src`, `type_`, `placeholder`, `value`
|
||
- [ ] `Html.Events.*`: `onClick`, `onInput`, `onSubmit`, `onBlur`, `onFocus`
|
||
- [ ] `Browser.element` — adds `init` returning `(model, Cmd msg)`, `subscriptions`
|
||
- [ ] Demo: counter app (`init=0`, `update Increment m = m+1`, `view` shows count + button)
|
||
|
||
### Phase 4 — Cmds and Subs
|
||
- [ ] `Cmd` — mapped to SX `perform` IO requests. `Cmd.none`, `Cmd.batch`
|
||
- [ ] `Http.get`/`Http.post` → SX fetch IO
|
||
- [ ] `Sub` — mapped to SX `dom-listen`. `Sub.none`, `Sub.batch`
|
||
- [ ] `Browser.Events.onClick`, `onKeyPress`, `onAnimationFrame`
|
||
- [ ] `Time.every` — periodic subscription via SX timer IO
|
||
- [ ] `Task.perform`/`Task.attempt` — single-shot async operations
|
||
|
||
### Phase 5 — standard library
|
||
- [ ] `String.*` — `length`, `append`, `concat`, `split`, `join`, `trim`, `toUpper`, `toLower`,
|
||
`contains`, `startsWith`, `endsWith`, `replace`, `toInt`, `toFloat`, `fromInt`, `fromFloat`
|
||
- [ ] `List.*` — `map`, `filter`, `foldl`, `foldr`, `head`, `tail`, `isEmpty`, `length`,
|
||
`reverse`, `append`, `concat`, `member`, `sort`, `sortBy`, `indexedMap`, `range`
|
||
- [ ] `Dict.*` — SX immutable dict; `fromList`, `toList`, `get`, `insert`, `remove`, `update`,
|
||
`member`, `keys`, `values`, `map`, `filter`, `foldl`
|
||
- [ ] `Set.*` — SX set primitive (Phase 18); `fromList`, `toList`, `member`, `insert`,
|
||
`remove`, `union`, `intersect`, `diff`
|
||
- [ ] `Maybe.*` — `withDefault`, `map`, `andThen`, `map2`
|
||
- [ ] `Result.*` — `withDefault`, `map`, `andThen`, `mapError`, `toMaybe`
|
||
- [ ] `Tuple.*` — `first`, `second`, `pair`, `mapFirst`, `mapSecond`
|
||
- [ ] `Basics.*` — `identity`, `always`, `not`, `xor`, `modBy`, `remainderBy`, `clamp`,
|
||
`min`, `max`, `abs`, `sqrt`, `logBase`, `e`, `pi`, `floor`, `ceiling`, `round`,
|
||
`truncate`, `toFloat`, `isNaN`, `isInfinite`, `compare`
|
||
- [ ] `Random.*` — seed-based PRNG via SX IO perform
|
||
|
||
### Phase 6 — full browser integration
|
||
- [ ] `Browser.application` — URL routing, `onUrlChange`, `onUrlRequest`
|
||
- [ ] `Browser.Navigation.*` — `pushUrl`, `replaceUrl`, `back`, `forward`
|
||
- [ ] `Url.Parser.*` — path segment parsing
|
||
- [ ] `Json.Decode.*` — JSON decoder combinators
|
||
- [ ] `Json.Encode.*` — JSON encoder
|
||
- [ ] `Ports` — `port` keyword; JS interop via SX `host-call`
|
||
|
||
## Blockers
|
||
|
||
_(none yet)_
|
||
|
||
## Progress log
|
||
|
||
_Newest first._
|
||
|
||
_(awaiting phase 1)_
|