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>
7.3 KiB
miniKanren-on-SX: relational programming on the CEK/VM
miniKanren is not a language to parse — it is an embedded DSL implemented as a library
of SX functions. No tokenizer, no transpiler. The entire system is a set of define forms
in lib/minikanren/. Programs are SX expressions using the miniKanren API.
The unique angle: SX's delimited continuation machinery (perform/cek-resume, call/cc)
maps almost perfectly to the search monad miniKanren needs. Backtracking is cooperative
suspension, not a separate trail machine. This is the cleanest possible host for miniKanren.
End-state goal: full core miniKanren (run, fresh, ==, conde, condu, onceo,
project, matche) + core.logic-style relations (appendo, membero, listo,
numbero, etc.) + arithmetic constraints (fd domain, CLP(FD) subset).
Ground rules
- Scope: only touch
lib/minikanren/**andplans/minikanren-on-sx.md. Do not editspec/,hosts/,shared/, or otherlib/<lang>/. - Shared-file issues go under "Blockers" below with a minimal repro; do not fix here.
- SX files: use
sx-treeMCP tools only. - Architecture: pure library — no source parser. Programs are written in SX using the API.
- Reference: The Reasoned Schemer (Friedman/Byrd/Kiselyov) + Byrd's dissertation.
- Commits: one feature per commit. Keep
## Progress logupdated and tick boxes.
Architecture sketch
SX program using miniKanren API
│
├── lib/minikanren/unify.sx — terms, variables, walk, unification, occurs check
├── lib/minikanren/substitution.sx — substitution as association list / hash table
├── lib/minikanren/stream.sx — lazy streams of substitutions (via delay/force)
├── lib/minikanren/goals.sx — == / fresh / conde / condu / onceo / project / matche
├── lib/minikanren/run.sx — run* / run n — drive the search, extract answers
├── lib/minikanren/relations.sx — standard relations: appendo, membero, listo, etc.
└── lib/minikanren/clpfd.sx — arithmetic constraints (CLP(FD) subset)
Key semantic mappings:
- Logic variable → SX vector of length 1 (mutable box);
make-varcreates fresh one;walkfollows the substitution chain - Substitution → SX association list (or hash table for performance) mapping var → term
- Stream of substitutions → lazy list using
delay/force(Phase 9 of primitives) - Goal → SX function
substitution → stream-of-substitutions ==→ unifies two terms, extending substitution or failing (empty stream)fresh→ introduces new logic variables;(fresh (x y) goal)→ goal with x, y boundconde→ interleave streams from multiple goal clauses (depth-first with interleaving)run n→ drive the stream, collect first n substitutions, reify answers
Roadmap
Phase 1 — variables + unification
make-var→ fresh logic variable (unique mutable box)var?v→ bool — is this a logic variable?walktermsubst→ follow substitution chain to ground term or unbound varwalk*termsubst→ deep walk (recurse into lists/dicts)unifyuvsubst→ extended substitution or#f(failure) Handles: var/var, var/term, term/var, list unification, number/string/symbol equality. No occurs check by default;unify-checkwith occurs check as opt-in.- Empty substitution
empty-s=(list)(empty assoc list) - Tests in
lib/minikanren/tests/unify.sx: ground terms, vars, lists, failure, occurs
Phase 2 — streams + goals
- Stream type:
mzero(empty stream =nil),unit s(singleton =(list s)),mplus(interleave two streams),bind(apply goal to stream) - Lazy streams via
delay/force— mature pairs for depth-first, immature for lazy ==goal:(fn (s) (let ((s2 (unify u v s))) (if s2 (unit s2) mzero)))succeed/fail— trivial goalsfresh—(fn (f) (fn (s) ((f (make-var)) s)))— introduces one var;fresh*for manyconde— interleaving disjunction of goal listscondu— committed choice (soft-cut): only explores first successful clauseonceo— succeeds at most once- Tests: basic goal composition, backtracking, interleaving
Phase 3 — run + reification
run*goal→ list of all answers (reified)run ngoal→ list of first n answersreifytermsubst→ replace unbound vars with_0,_1, ... namesreify-s→ builds reification substitution for naming unbound vars consistentlyfreshwith multiple variables:(fresh (x y z) goal)sugar- Query variable conventions:
qas canonical query variable - Tests: classic miniKanren programs —
(run* q (== q 1))→(1),(run* q (conde ((== q 1)) ((== q 2))))→(1 2), Peano arithmetic,appendopreview
Phase 4 — standard relations
appendolsls— list append, runs forwards and backwardsmemberoxl— x is a member of llistol— l is a proper listnullol— l is emptypairop— p is a pair (cons cell)caropa— car of paircdropd— cdr of pairconsoadp— consfirsto/resto— aliases for caro/cdroreverseolr— reverse of listflattenolf— flatten nested listspermuteolp— permutation of listlengtholn— length as a relation (Peano or integer)- Tests: run each relation forwards and backwards; generate from partial inputs
Phase 5 — project + matche + negation
project(x ...) body— access reified values of logic vars inside a goal; escapes to ground values for arithmetic or string opsmatche— pattern matching over logic terms (extension from core.logic)(matche l ((head . tail) goal) (() goal))conda— soft-cut disjunction (like Prolog->)condu— committed choice (already in phase 2; refine semantics here)nafc— negation as finite failure with constraint- Tests: Zebra puzzle, N-queens, Sudoku via
project, family relations viamatche
Phase 6 — arithmetic constraints CLP(FD)
- Finite domain variables:
fd-varwith domain[lo..hi] inxdomain— constrain x to domainfd-eqxy— x = y (constraint propagation)fd-neqxy— x ≠ yfd-ltfd-ltefd-gtfd-gte— ordering constraintsfd-plusxyz— x + y = z (constraint)fd-timesxyz— x * y = z- Arc consistency propagation — when domain narrows, propagate to constrained vars
- Labelling:
fd-rundrives search by splitting domains when propagation stalls - Tests: send-more-money, N-queens with CLP(FD), map coloring, cryptarithmetic
Phase 7 — tabling (memoization of relations)
tabledannotation: memoize calls to a relation using a hash table- Prevents infinite loops in recursive relations like
pathoon cyclic graphs - Producer/consumer scheduling for tabled relations (variant of SLG resolution)
- Tests: cyclic graph reachability, mutual recursion, Fibonacci via tabling
Blockers
(none yet)
Progress log
Newest first.
(awaiting phase 1)