Fix scopes page: rename &key code param that shadowed HTML <code> tag

The ~geography/scopes-demo-example component had (&key demo code), and
its body used (code code) — the parameter shadowed the HTML tag, causing
"Undefined symbol: code" at render time. Renamed to (&key demo src).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 18:04:06 +00:00
parent aa508bad77
commit b9d30749f7

View File

@@ -1,194 +1,302 @@
;; --------------------------------------------------------------------------- (defcomp
;; Scopes — the unified primitive beneath provide, collect!, and spreads ~geography/demo-scope-basic
;; --------------------------------------------------------------------------- ()
(div
:class "space-y-2"
;; ---- Demo components ---- (scope
"demo-theme"
(defcomp ~geography/demo-scope-basic () :value "violet"
(div :class "space-y-2" (div
(scope "demo-theme" :value "violet" :class "rounded-lg p-3 bg-violet-50 border border-violet-200"
(div :class "rounded-lg p-3 bg-violet-50 border border-violet-200" (p
(p :class "text-sm text-violet-800 font-semibold" :class "text-sm text-violet-800 font-semibold"
(str "Inside scope: theme = " (context "demo-theme"))) (str "Inside scope: theme = " (context "demo-theme")))
(p :class "text-xs text-stone-500" "scope creates a named scope. context reads it."))) (p
(div :class "rounded-lg p-3 bg-stone-50 border border-stone-200" :class "text-xs text-stone-500"
(p :class "text-sm text-stone-600" "Outside scope: no context available.")))) "scope creates a named scope. context reads it.")))
(div
:class "rounded-lg p-3 bg-stone-50 border border-stone-200"
(p
:class "text-sm text-stone-600"
"Outside scope: no context available."))))
(defcomp ~geography/demo-scope-emit () (defcomp
(div :class "space-y-2" ~geography/demo-scope-emit
(scope "demo-deps" ()
(div :class "rounded-lg p-3 bg-stone-50 border border-stone-200" (div
(p :class "text-sm text-stone-700" :class "space-y-2"
(scope
"demo-deps"
(div
:class "rounded-lg p-3 bg-stone-50 border border-stone-200"
(p
:class "text-sm text-stone-700"
(emit! "demo-deps" "lodash") (emit! "demo-deps" "lodash")
(emit! "demo-deps" "react") (emit! "demo-deps" "react")
"Components emit their dependencies upward.")) "Components emit their dependencies upward."))
(div :class "rounded-lg p-3 bg-violet-50 border border-violet-200" (div
:class "rounded-lg p-3 bg-violet-50 border border-violet-200"
(p :class "text-sm text-violet-800 font-semibold" "Emitted:") (p :class "text-sm text-violet-800 font-semibold" "Emitted:")
(ul :class "text-xs text-stone-600 list-disc pl-5" (ul
:class "text-xs text-stone-600 list-disc pl-5"
(map (fn (d) (li (code d))) (emitted "demo-deps"))))))) (map (fn (d) (li (code d))) (emitted "demo-deps")))))))
(defcomp ~geography/demo-scope-dedup () (defcomp
(div :class "space-y-2" ~geography/demo-scope-dedup
(div :class "rounded-lg p-3 bg-stone-50 border border-stone-200" ()
(p :class "text-sm text-stone-700" (div
:class "space-y-2"
(div
:class "rounded-lg p-3 bg-stone-50 border border-stone-200"
(p
:class "text-sm text-stone-700"
(collect! "demo-css-dedup" ".card { padding: 1rem }") (collect! "demo-css-dedup" ".card { padding: 1rem }")
(collect! "demo-css-dedup" ".card { padding: 1rem }") (collect! "demo-css-dedup" ".card { padding: 1rem }")
(collect! "demo-css-dedup" ".btn { color: blue }") (collect! "demo-css-dedup" ".btn { color: blue }")
"Three collect! calls, two identical. Only unique values kept.")) "Three collect! calls, two identical. Only unique values kept."))
(div :class "rounded-lg p-3 bg-violet-50 border border-violet-200" (div
(p :class "text-sm text-violet-800 font-semibold" :class "rounded-lg p-3 bg-violet-50 border border-violet-200"
(p
:class "text-sm text-violet-800 font-semibold"
(str "Collected: " (len (collected "demo-css-dedup")) " rules")) (str "Collected: " (len (collected "demo-css-dedup")) " rules"))
(ul :class "text-xs text-stone-600 list-disc pl-5" (ul
:class "text-xs text-stone-600 list-disc pl-5"
(map (fn (r) (li (code r))) (collected "demo-css-dedup")))))) (map (fn (r) (li (code r))) (collected "demo-css-dedup"))))))
(defcomp
;; ---- Layout helper ---- ~geography/scopes-demo-example
(&key demo src)
(defcomp ~geography/scopes-demo-example (&key demo code) (div
(div :class "grid grid-cols-1 lg:grid-cols-2 gap-4 my-6 items-start" :class "grid grid-cols-1 lg:grid-cols-2 gap-4 my-6 items-start"
(div :class "border border-dashed border-stone-300 rounded-lg p-4 bg-stone-50 min-h-[80px]" (div
:class "border border-dashed border-stone-300 rounded-lg p-4 bg-stone-50 min-h-[80px]"
demo) demo)
(div :class "not-prose bg-stone-100 rounded-lg p-4 overflow-x-auto" (div
(pre :class "text-sm leading-relaxed whitespace-pre-wrap break-words" (code code))))) :class "not-prose bg-stone-100 rounded-lg p-4 overflow-x-auto"
(pre
:class "text-sm leading-relaxed whitespace-pre-wrap break-words"
(code src)))))
(defcomp
;; ---- Page content ---- ~geography/scopes-content
()
(defcomp ~geography/scopes-content () (~docs/page
(~docs/page :title "Scopes" :title "Scopes"
(p
(p :class "text-stone-500 text-sm italic mb-8" :class "text-stone-500 text-sm italic mb-8"
"The unified primitive. " (code "scope") " creates a named scope with an optional value " "The unified primitive. "
"and an accumulator. " (code "provide") ", " (code "collect!") ", spreads, islands — " (code "scope")
" creates a named scope with an optional value "
"and an accumulator. "
(code "provide")
", "
(code "collect!")
", spreads, islands — "
"they all resolve to scope operations at the platform level.") "they all resolve to scope operations at the platform level.")
(~docs/section
;; ===================================================================== :title "The primitive"
;; I. The primitive :id "primitive"
;; ===================================================================== (p
(code "scope")
(~docs/section :title "The primitive" :id "primitive" " is a special form that pushes a named scope, evaluates its body, "
(p (code "scope") " is a special form that pushes a named scope, evaluates its body, "
"then pops it. The scope has three properties: a name, a downward value, and an " "then pops it. The scope has three properties: a name, a downward value, and an "
"upward accumulator.") "upward accumulator.")
(~docs/code
(~docs/code :src (highlight "(scope name body...) ;; scope with no value\n(scope name :value v body...) ;; scope with downward value" "lisp")) :src (highlight
"(scope name body...) ;; scope with no value\n(scope name :value v body...) ;; scope with downward value"
(p "Within the body, " (code "context") " reads the value, " (code "emit!") " appends " "lisp"))
"to the accumulator, and " (code "emitted") " reads what was accumulated.") (p
"Within the body, "
(code "context")
" reads the value, "
(code "emit!")
" appends "
"to the accumulator, and "
(code "emitted")
" reads what was accumulated.")
(~geography/scopes-demo-example (~geography/scopes-demo-example
:demo (~geography/demo-scope-basic) :demo (~geography/demo-scope-basic)
:code (highlight "(scope \"theme\" :value \"violet\"\n (context \"theme\")) ;; → \"violet\"\n\n;; Nested scopes shadow:\n(scope \"x\" :value \"outer\"\n (scope \"x\" :value \"inner\"\n (context \"x\")) ;; → \"inner\"\n (context \"x\")) ;; → \"outer\"" "lisp"))) :src (highlight
"(scope \"theme\" :value \"violet\"\n (context \"theme\")) ;; → \"violet\"\n\n;; Nested scopes shadow:\n(scope \"x\" :value \"outer\"\n (scope \"x\" :value \"inner\"\n (context \"x\")) ;; → \"inner\"\n (context \"x\")) ;; → \"outer\""
;; ===================================================================== "lisp")))
;; II. Sugar forms (~docs/section
;; ===================================================================== :title "Sugar forms"
:id "sugar"
(~docs/section :title "Sugar forms" :id "sugar" (p
"Nobody writes "
(p "Nobody writes " (code "scope") " directly. The sugar forms are the API:") (code "scope")
" directly. The sugar forms are the API:")
(~docs/table (~docs/table
:headers (list "Sugar" "Expands to" "Used for") :headers (list "Sugar" "Expands to" "Used for")
:rows (list :rows (list
(list "provide" "(scope name :value v body...)" "Downward context passing") (list
(list "collect!" "Lazy root scope + dedup emit" "CSS rule accumulation") "provide"
(list "Spreads" "(scope \"element-attrs\" ...)" "Child-to-parent attrs (implicit)"))) "(scope name :value v body...)"
"Downward context passing")
(~docs/subsection :title "provide — scope with a value" (list
(p (code "(provide name value body...)") " is exactly " "collect!"
(code "(scope name :value value body...)") ". It exists because " "Lazy root scope + dedup emit"
"CSS rule accumulation")
(list
"Spreads"
"(scope \"element-attrs\" ...)"
"Child-to-parent attrs (implicit)")))
(~docs/subsection
:title "provide — scope with a value"
(p
(code "(provide name value body...)")
" is exactly "
(code "(scope name :value value body...)")
". It exists because "
"the two-arg form is the common case.") "the two-arg form is the common case.")
(~docs/code :src (highlight ";; These are equivalent:\n(provide \"theme\" {:primary \"violet\"}\n (h1 \"hello\"))\n\n(scope \"theme\" :value {:primary \"violet\"}\n (h1 \"hello\"))" "lisp"))) (~docs/code
:src (highlight
(~docs/subsection :title "collect! — lazy root scope with dedup" ";; These are equivalent:\n(provide \"theme\" {:primary \"violet\"}\n (h1 \"hello\"))\n\n(scope \"theme\" :value {:primary \"violet\"}\n (h1 \"hello\"))"
(p (code "collect!") " is the most interesting sugar. When called, if no scope exists " "lisp")))
(~docs/subsection
:title "collect! — lazy root scope with dedup"
(p
(code "collect!")
" is the most interesting sugar. When called, if no scope exists "
"for that name, it lazily creates a root scope with deduplication enabled. " "for that name, it lazily creates a root scope with deduplication enabled. "
"Then it emits into it.") "Then it emits into it.")
(~geography/scopes-demo-example (~geography/scopes-demo-example
:demo (~geography/demo-scope-dedup) :demo (~geography/demo-scope-dedup)
:code (highlight ";; collect! creates a lazy root scope:\n(collect! \"css\" \".card { pad: 1rem }\")\n(collect! \"css\" \".card { pad: 1rem }\") ;; deduped!\n(collect! \"css\" \".btn { color: blue }\")\n(collected \"css\") ;; → 2 rules\n\n;; Equivalent to:\n(scope \"css\" ;; with dedup\n (emit! \"css\" ...)\n (emitted \"css\"))" "lisp")) :src (highlight
(p (code "collected") " is an alias for " (code "emitted") ". " ";; collect! creates a lazy root scope:\n(collect! \"css\" \".card { pad: 1rem }\")\n(collect! \"css\" \".card { pad: 1rem }\") ;; deduped!\n(collect! \"css\" \".btn { color: blue }\")\n(collected \"css\") ;; → 2 rules\n\n;; Equivalent to:\n(scope \"css\" ;; with dedup\n (emit! \"css\" ...)\n (emitted \"css\"))"
(code "clear-collected!") " clears the accumulator.")) "lisp"))
(p
(~docs/subsection :title "Spreads — implicit element scope" (code "collected")
(p "Every element rendering function wraps its children in " " is an alias for "
(code "(scope-push! \"element-attrs\" nil)") ". Spread children " (code "emitted")
(code "emit!") " their attrs into this scope. After rendering, the element " ". "
(code "clear-collected!")
" clears the accumulator."))
(~docs/subsection
:title "Spreads — implicit element scope"
(p
"Every element rendering function wraps its children in "
(code "(scope-push! \"element-attrs\" nil)")
". Spread children "
(code "emit!")
" their attrs into this scope. After rendering, the element "
"merges the emitted attrs.") "merges the emitted attrs.")
(p "See the " (p
(a :href "/sx/(geography.(spreads))" :class "text-violet-600 hover:underline" "spreads article") "See the "
(a
:href "/sx/(geography.(spreads))"
:class "text-violet-600 hover:underline"
"spreads article")
" for the full mechanism."))) " for the full mechanism.")))
(~docs/section
;; ===================================================================== :title "Upward data flow"
;; III. Accumulator: upward data flow :id "upward"
;; =====================================================================
(~docs/section :title "Upward data flow" :id "upward"
(~geography/scopes-demo-example (~geography/scopes-demo-example
:demo (~geography/demo-scope-emit) :demo (~geography/demo-scope-emit)
:code (highlight "(scope \"deps\"\n (emit! \"deps\" \"lodash\")\n (emit! \"deps\" \"react\")\n (emitted \"deps\")) ;; → (\"lodash\" \"react\")" "lisp")) :src (highlight
"(scope \"deps\"\n (emit! \"deps\" \"lodash\")\n (emit! \"deps\" \"react\")\n (emitted \"deps\")) ;; → (\"lodash\" \"react\")"
(p "Accumulation always goes to the " (em "nearest") " enclosing scope with that name. " "lisp"))
(p
"Accumulation always goes to the "
(em "nearest")
" enclosing scope with that name. "
"This is what makes nested elements work — a spread inside a nested " "This is what makes nested elements work — a spread inside a nested "
(code "span") " emits to the " (code "span") "'s scope, not an outer " (code "span")
(code "div") "'s scope.")) " emits to the "
(code "span")
;; ===================================================================== "'s scope, not an outer "
;; IV. Platform implementation (code "div")
;; ===================================================================== "'s scope."))
(~docs/section
(~docs/section :title "Platform implementation" :id "platform" :title "Platform implementation"
:id "platform"
(p "Each platform (Python, JavaScript) maintains a single data structure:") (p
"Each platform (Python, JavaScript) maintains a single data structure:")
(~docs/code :src (highlight "_scope_stacks = {} ;; {name: [{value, emitted: [], dedup: bool}]}" "python")) (~docs/code
:src (highlight
"_scope_stacks = {} ;; {name: [{value, emitted: [], dedup: bool}]}"
"python"))
(p "Six operations on this structure:") (p "Six operations on this structure:")
(~docs/table (~docs/table
:headers (list "Operation" "Purpose") :headers (list "Operation" "Purpose")
:rows (list :rows (list
(list "scope-push!(name, value)" "Push {value, emitted: [], dedup: false}") (list
"scope-push!(name, value)"
"Push {value, emitted: [], dedup: false}")
(list "scope-pop!(name)" "Pop the most recent scope") (list "scope-pop!(name)" "Pop the most recent scope")
(list "context(name, default?)" "Read value from nearest scope") (list "context(name, default?)" "Read value from nearest scope")
(list "emit!(name, value)" "Append to nearest scope's accumulator (respects dedup)") (list
"emit!(name, value)"
"Append to nearest scope's accumulator (respects dedup)")
(list "emitted(name)" "Read accumulated values from nearest scope") (list "emitted(name)" "Read accumulated values from nearest scope")
(list "collect!(name, value)" "Lazy push root scope with dedup, then emit"))) (list
"collect!(name, value)"
(p (code "provide-push!") " and " (code "provide-pop!") " are aliases for " "Lazy push root scope with dedup, then emit")))
(code "scope-push!") " and " (code "scope-pop!") ". " (p
"All adapter code uses " (code "scope-push!") "/" (code "scope-pop!") " directly.")) (code "provide-push!")
" and "
;; ===================================================================== (code "provide-pop!")
;; V. Unification " are aliases for "
;; ===================================================================== (code "scope-push!")
" and "
(~docs/section :title "What scope unifies" :id "unification" (code "scope-pop!")
". "
"All adapter code uses "
(code "scope-push!")
"/"
(code "scope-pop!")
" directly."))
(~docs/section
:title "What scope unifies"
:id "unification"
(p "Before scopes, the platform had two separate mechanisms:") (p "Before scopes, the platform had two separate mechanisms:")
(~docs/code
(~docs/code :src (highlight ";; Before: two mechanisms\n_provide_stacks = {} ;; {name: [{value, emitted: []}]}\n_collect_buckets = {} ;; {name: [values...]}\n\n;; After: one mechanism\n_scope_stacks = {} ;; {name: [{value, emitted: [], dedup: bool}]}" "python")) :src (highlight
";; Before: two mechanisms\n_provide_stacks = {} ;; {name: [{value, emitted: []}]}\n_collect_buckets = {} ;; {name: [values...]}\n\n;; After: one mechanism\n_scope_stacks = {} ;; {name: [{value, emitted: [], dedup: bool}]}"
"python"))
(p "The unification is not just code cleanup. It means:") (p "The unification is not just code cleanup. It means:")
(ul :class "space-y-1" (ul
(li (code "collect!") " can be nested inside " (code "provide") " scopes — " :class "space-y-1"
(li
(code "collect!")
" can be nested inside "
(code "provide")
" scopes — "
"they share the same stack.") "they share the same stack.")
(li "A component can " (code "emit!") " and " (code "collect!") " into the same scope — " (li
"A component can "
(code "emit!")
" and "
(code "collect!")
" into the same scope — "
"they use the same accumulator.") "they use the same accumulator.")
(li "The dedup flag is per-scope, not per-mechanism — a " (code "provide") " scope " (li
"has no dedup, a " (code "collect!") " root scope has dedup.")) "The dedup flag is per-scope, not per-mechanism — a "
(code "provide")
(p "See the " " scope "
(a :href "/sx/(etc.(plan.scoped-effects))" :class "text-violet-600 hover:underline" "scoped effects plan") "has no dedup, a "
(code "collect!")
" root scope has dedup."))
(p
"See the "
(a
:href "/sx/(etc.(plan.scoped-effects))"
:class "text-violet-600 hover:underline"
"scoped effects plan")
" for the full design rationale and future phases (reactive scopes, morph scopes).") " for the full design rationale and future phases (reactive scopes, morph scopes).")
(~docs/note (~docs/note
(p (strong "Spec: ") "The " (code "scope") " special form is in " (p
(a :href "/sx/(language.(spec.(explore.evaluator)))" :class "font-mono text-violet-600 hover:underline text-sm" "eval.sx") (strong "Spec: ")
"The "
(code "scope")
" special form is in "
(a
:href "/sx/(language.(spec.(explore.evaluator)))"
:class "font-mono text-violet-600 hover:underline text-sm"
"eval.sx")
". Platform primitives are declared in " ". Platform primitives are declared in "
(a :href "/sx/(language.(spec.(explore.boundary)))" :class "font-mono text-violet-600 hover:underline text-sm" "boundary.sx") (a
:href "/sx/(language.(spec.(explore.boundary)))"
:class "font-mono text-violet-600 hover:underline text-sm"
"boundary.sx")
" (Tier 5: Scoped effects)."))))) " (Tier 5: Scoped effects).")))))