Add provide/emit! geography article, update spreads article, fix foundations rendering
- New geography article (provide.sx): four primitives, demos, nested scoping, adapter comparison, spec explorer links - Updated spreads article section VI: provide/emit! is now implemented, not planned - Fixed foundations.sx: ~docs/code-block → ~docs/code (undefined component was causing the page to silently fail to render) - Added nav entry and defpage route for provide/emit! article Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -244,37 +244,43 @@
|
||||
(p "Spread and collect are both instances of the same pattern: "
|
||||
(strong "child communicates upward through the render tree") ". "
|
||||
"The general form is " (code "provide") "/" (code "context") "/" (code "emit!")
|
||||
" — render-time dynamic scope.")
|
||||
" — render-time dynamic scope. Spreads are now implemented on top of this mechanism.")
|
||||
|
||||
(~docs/subsection :title "How spreads use provide/emit! internally"
|
||||
(p "Every element rendering wraps its children in a provider scope named "
|
||||
(code "\"element-attrs\"") ". When the renderer encounters a spread child, it "
|
||||
"emits the spread's attrs into this scope instead of returning a DOM node. "
|
||||
"After all children render, the element collects emitted attrs and merges them.")
|
||||
(~docs/code :code (highlight ";; What happens inside element rendering:\n;; 1. Element creates a provider scope\n(provide-push! \"element-attrs\" nil)\n\n;; 2. Children render — spreads emit into scope\n;; (make-spread {:class \"card\"}) → (emit! \"element-attrs\" {:class \"card\"})\n\n;; 3. Element collects emitted attrs\n(for-each\n (fn (spread-dict) (merge-spread-attrs attrs spread-dict))\n (emitted \"element-attrs\"))\n(provide-pop! \"element-attrs\")" "lisp"))
|
||||
(p (code "emit!") " is tolerant — when no provider exists (a spread outside any element), "
|
||||
"it silently returns nil. This means spreads in non-element contexts "
|
||||
"(fragments, " (code "begin") ", bare " (code "map") ") vanish without error.")
|
||||
(p "Stored spreads work naturally:")
|
||||
(~docs/code :code (highlight ";; Spread value stored in a let binding\n(let ((card (make-spread {:class \"card\"})))\n (div card \"hello\"))\n;; → <div class=\"card\">hello</div>\n;;\n;; `card` holds a _Spread object.\n;; When div renders it, the adapter sees type \"spread\"\n;; → emits into div's \"element-attrs\" provider\n;; → returns \"\" (no content)\n;; div merges the emitted attrs after children." "lisp")))
|
||||
|
||||
(~docs/subsection :title "The unification"
|
||||
(~docs/table
|
||||
:headers (list "Current" "General form" "Direction")
|
||||
:headers (list "Mechanism" "General form" "Direction")
|
||||
:rows (list
|
||||
(list "collect! / collected" "emit! / emitted" "upward (child → scope)")
|
||||
(list "make-spread" "emit! into implicit parent-attrs provider" "upward (child → parent)")
|
||||
(list "(nothing yet)" "context" "downward (scope → child)")))
|
||||
(p (code "provide") " creates a named scope with a value (downward) and an accumulator (upward). "
|
||||
(code "context") " reads the value. " (code "emit!") " appends to the accumulator. "
|
||||
(code "emitted") " retrieves accumulated values.")
|
||||
(~docs/code :code (highlight ";; Downward: theme context\n(provide \"theme\" {:primary \"violet\" :font \"serif\"}\n (h1 :style (str \"color:\" (get (context \"theme\") :primary))\n \"Themed heading\"))\n\n;; Upward: script accumulation (like collect!)\n(provide \"scripts\" nil\n (div\n (emit! \"scripts\" \"analytics.js\")\n (div (emit! \"scripts\" \"charts.js\") \"chart\"))\n (for-each (fn (s) (script :src s)) (emitted \"scripts\")))\n\n;; Both at once\n(provide \"page\" {:title \"Home\"}\n (h1 (context \"page\" :title))\n (emit! \"page\" {:meta \"og:title\" :content \"Home\"})\n (for-each (fn (m) (meta :name (get m :meta) :content (get m :content)))\n (emitted \"page\")))" "lisp")))
|
||||
|
||||
(~docs/subsection :title "What this means"
|
||||
(p "Three mechanisms collapse into one:")
|
||||
(ul :class "list-disc pl-5 space-y-1 text-stone-600"
|
||||
(li (code "collect!") " = " (code "emit!") " with deduplication")
|
||||
(li (code "spread") " = " (code "emit!") " into implicit parent-attrs provider")
|
||||
(li (code "collected") " = " (code "emitted"))
|
||||
(li (code "context") " = downward data flow (new capability — no current equivalent)"))
|
||||
(p "The reactive-spread we just built would naturally follow — the effect tracks "
|
||||
"signal deps in the " (code "emit!") " call, the provider accumulates, the element applies.")
|
||||
(p :class "text-stone-500 italic"
|
||||
"This is the planned next step. The current primitives (spread, collect, reactive-spread) "
|
||||
"work and are fully orthogonal. " (code "provide/context/emit!") " will be the deeper "
|
||||
"foundation they are reimplemented on top of."))
|
||||
(list "spread" "emit! into element-attrs provider" "upward (child → parent)")
|
||||
(list "theme / config" "context" "downward (scope → child)")))
|
||||
(p "Three mechanisms, one substrate. "
|
||||
(a :href "/sx/(geography.(provide))" :class "text-violet-600 hover:underline"
|
||||
"See the full provide/context/emit! article")
|
||||
" for the general primitive and its other uses."))
|
||||
|
||||
(~docs/note
|
||||
(p (strong "Plan: ") (code "provide") "/" (code "context") "/" (code "emit!") " is specced "
|
||||
"and ready to implement. Per-name stacks. Each entry has a value and an emitted list. "
|
||||
"Four primitives: " (code "provide") " (special form), " (code "context") ", "
|
||||
(code "emit!") ", " (code "emitted") ". Platform provides " (code "provide-push!")
|
||||
"/" (code "provide-pop!") ". See the implementation plan for details.")))))
|
||||
(p (strong "Spec: ") "The provide/emit! primitives are declared in "
|
||||
(a :href "/sx/(language.(spec.(explore.boundary)))" :class "font-mono text-violet-600 hover:underline text-sm" "boundary.sx")
|
||||
" (Tier 5: Dynamic scope). The " (code "provide") " special form is in "
|
||||
(a :href "/sx/(language.(spec.(explore.evaluator)))" :class "font-mono text-violet-600 hover:underline text-sm" "eval.sx")
|
||||
". Element rendering with provide/emit! is visible in all four adapter specs: "
|
||||
(a :href "/sx/(language.(spec.(explore.adapter-html)))" :class "font-mono text-violet-600 hover:underline text-sm" "adapter-html")
|
||||
", "
|
||||
(a :href "/sx/(language.(spec.(explore.adapter-async)))" :class "font-mono text-violet-600 hover:underline text-sm" "adapter-async")
|
||||
", "
|
||||
(a :href "/sx/(language.(spec.(explore.adapter-sx)))" :class "font-mono text-violet-600 hover:underline text-sm" "adapter-sx")
|
||||
", "
|
||||
(a :href "/sx/(language.(spec.(explore.adapter-dom)))" :class "font-mono text-violet-600 hover:underline text-sm" "adapter-dom")
|
||||
".")))))
|
||||
|
||||
Reference in New Issue
Block a user