Transparent lazy module loading — code loads like data
When the VM or CEK hits an undefined symbol, it checks a symbol→library index (built from manifest exports at boot), loads the library that exports it, and returns the value. Execution continues as if the module was always loaded. No import statements, no load-library! calls, no Suspense boundaries — just call the function. This is the same mechanism as IO suspension for data fetching. The programmer doesn't distinguish between calling a local function and calling one that needs its module fetched first. The runtime treats code as just another resource. Implementation: - _symbol_resolve_hook in sx_types.ml — called by env_get_id (CEK path) and vm_global_get (VM path) when a symbol isn't found - Symbol→library index built from manifest exports in sx-platform.js - __resolve-symbol native calls __sxLoadLibrary, module loads, symbol appears in globals, execution resumes - compile-modules.js extracts export lists into module-manifest.json - Playground page demonstrates: (freeze-scope) triggers freeze.sxbc download transparently on first use 2650/2650 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,4 +3,5 @@
|
||||
(define tools-nav-items
|
||||
(list
|
||||
(dict :label "SX Tools" :href "/sx/(tools.(sx-tools))")
|
||||
(dict :label "Services" :href "/sx/(tools.(services))")))
|
||||
(dict :label "Services" :href "/sx/(tools.(services))")
|
||||
(dict :label "Playground" :href "/sx/(tools.(playground))")))
|
||||
|
||||
@@ -625,6 +625,15 @@
|
||||
|
||||
(define tools (fn (content) (or content "")))
|
||||
|
||||
(define
|
||||
playground
|
||||
(fn
|
||||
(&key title &rest args)
|
||||
(quasiquote
|
||||
(~playground/content
|
||||
:title (unquote (or title "Playground"))
|
||||
(splice-unquote args)))))
|
||||
|
||||
(define
|
||||
services
|
||||
(fn
|
||||
|
||||
@@ -1,53 +1,52 @@
|
||||
;; Playground — demonstrates lazy module loading on navigation
|
||||
;; Playground — demonstrates transparent lazy module loading
|
||||
|
||||
(defcomp ~playground/content (&key (title "Playground"))
|
||||
(~docs/page :title title
|
||||
(h1 "Playground")
|
||||
(p "This page lazy-loads the " (code "sx freeze") " and " (code "sx compiler")
|
||||
" modules when you navigate here. They are NOT loaded at boot.")
|
||||
(p "An interactive SX REPL. The " (code "sx freeze") " module "
|
||||
"loads transparently when this island hydrates — no import needed.")
|
||||
(span :data-sx-island "playground/repl"
|
||||
(div (~tw :tokens "mt-6 border border-stone-200 rounded-lg overflow-hidden")
|
||||
(div (~tw :tokens "bg-stone-100 px-4 py-2 text-sm font-mono text-stone-500")
|
||||
"sx> ")
|
||||
(div (~tw :tokens "p-4 font-mono text-sm min-h-[120px] text-stone-400")
|
||||
"Loading modules...")))))
|
||||
"Loading...")))))
|
||||
|
||||
(defisland ~playground/repl ()
|
||||
(do
|
||||
;; These two modules are NOT in the boot manifest — they load on demand
|
||||
(load-library! "sx freeze")
|
||||
(load-library! "sx compiler")
|
||||
(let ((input (signal ""))
|
||||
(output (signal "(ready)"))
|
||||
(history (signal (list))))
|
||||
(let ((eval-input
|
||||
(fn (e)
|
||||
(let ((src (deref input)))
|
||||
(when (not (empty? src))
|
||||
(let ((result (cek-try
|
||||
(fn () (str (cek-eval (first (sx-parse src)))))
|
||||
(fn (err) (str "Error: " err)))))
|
||||
(reset! output result)
|
||||
(swap! history (fn (h) (append h (list (dict :src src :result result)))))
|
||||
(reset! input "")))))))
|
||||
(div (~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(div (~tw :tokens "bg-stone-800 text-stone-100 p-4 font-mono text-sm min-h-[120px] max-h-[300px] overflow-y-auto")
|
||||
(for-each
|
||||
(fn (entry)
|
||||
(div
|
||||
(div (~tw :tokens "text-emerald-400") (str "sx> " (get entry "src")))
|
||||
(div (~tw :tokens "text-stone-300 mb-2") (get entry "result"))))
|
||||
(deref history))
|
||||
(div (~tw :tokens "text-amber-400") (str "=> " (deref output))))
|
||||
(div (~tw :tokens "flex border-t border-stone-200")
|
||||
(span (~tw :tokens "bg-stone-100 px-3 py-2 text-stone-500 font-mono text-sm") "sx>")
|
||||
(input :type "text"
|
||||
:value (deref input)
|
||||
:on-input (fn (e) (reset! input (element-value (host-get e "target"))))
|
||||
:on-keydown (fn (e) (when (= (host-get e "key") "Enter") (eval-input e)))
|
||||
(~tw :tokens "flex-1 px-3 py-2 font-mono text-sm outline-none")
|
||||
:placeholder "Type an expression..."
|
||||
:autofocus "true")
|
||||
(button :on-click eval-input
|
||||
(~tw :tokens "px-4 py-2 bg-violet-600 text-white font-mono text-sm hover:bg-violet-700")
|
||||
"Eval")))))))
|
||||
(let ((input (signal ""))
|
||||
(output (signal "(ready)"))
|
||||
(history (signal (list)))
|
||||
(modules-loaded (signal
|
||||
(str "freeze: " (type-of freeze-registry)))))
|
||||
(let ((eval-input
|
||||
(fn (e)
|
||||
(let ((src (deref input)))
|
||||
(when (not (empty? src))
|
||||
(let ((result (cek-try
|
||||
(fn () (str (cek-eval (first (sx-parse src)))))
|
||||
(fn (err) (str "Error: " err)))))
|
||||
(reset! output result)
|
||||
(swap! history (fn (h) (append h (list (dict :src src :result result)))))
|
||||
(reset! input "")))))))
|
||||
(div (~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(div (~tw :tokens "bg-stone-800 text-stone-100 p-4 font-mono text-sm min-h-[120px] max-h-[300px] overflow-y-auto")
|
||||
(for-each
|
||||
(fn (entry)
|
||||
(div
|
||||
(div (~tw :tokens "text-emerald-400") (str "sx> " (get entry "src")))
|
||||
(div (~tw :tokens "text-stone-300 mb-2") (get entry "result"))))
|
||||
(deref history))
|
||||
(div (~tw :tokens "text-amber-400") (str "=> " (deref output)))
|
||||
(div (~tw :tokens "text-stone-600 text-xs mt-2") (deref modules-loaded)))
|
||||
(div (~tw :tokens "flex border-t border-stone-200")
|
||||
(span (~tw :tokens "bg-stone-100 px-3 py-2 text-stone-500 font-mono text-sm") "sx>")
|
||||
(input :type "text"
|
||||
:value (deref input)
|
||||
:on-input (fn (e) (reset! input (element-value (host-get e "target"))))
|
||||
:on-keydown (fn (e) (when (= (host-get e "key") "Enter") (eval-input e)))
|
||||
(~tw :tokens "flex-1 px-3 py-2 font-mono text-sm outline-none")
|
||||
:placeholder "Type an expression... (try: (+ 1 2) or (map inc (list 1 2 3)))"
|
||||
:autofocus "true")
|
||||
(button :on-click eval-input
|
||||
(~tw :tokens "px-4 py-2 bg-violet-600 text-white font-mono text-sm hover:bg-violet-700")
|
||||
"Eval"))))))
|
||||
|
||||
Reference in New Issue
Block a user