Add (param :as type) annotations to all fn/lambda params across SX spec

Extend the type annotation system from defcomp-only to fn/lambda params:
- Infrastructure: sf-lambda, py/js-collect-params-loop, and bootstrap_py.py
  now recognize (name :as type) in param lists, extracting just the name
- bootstrap_py.py: add _extract_param_name() helper, fix _emit_for_each_stmt
- 521 type annotations across 22 .sx spec files (eval, types, adapters,
  transpilers, engine, orchestration, deps, signals, router, prove, etc.)
- Zero behavioral change: annotations are metadata for static analysis only
- All bootstrappers (Python, JS, G1) pass, 81/81 spec tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 20:27:36 +00:00
parent c82941d93c
commit b99e69d1bb
23 changed files with 532 additions and 498 deletions

View File

@@ -73,7 +73,7 @@
;; --------------------------------------------------------------------------
(define eval-expr
(fn (expr env)
(fn (expr (env :as dict))
(case (type-of expr)
;; --- literals pass through ---
@@ -116,7 +116,7 @@
;; --------------------------------------------------------------------------
(define eval-list
(fn (expr env)
(fn (expr (env :as dict))
(let ((head (first expr))
(args (rest expr)))
@@ -191,7 +191,7 @@
;; --------------------------------------------------------------------------
(define eval-call
(fn (head args env)
(fn (head (args :as list) (env :as dict))
(let ((f (trampoline (eval-expr head env)))
(evaluated-args (map (fn (a) (trampoline (eval-expr a env))) args)))
(cond
@@ -215,7 +215,7 @@
(define call-lambda
(fn (f args caller-env)
(fn ((f :as lambda) (args :as list) (caller-env :as dict))
(let ((params (lambda-params f))
(local (env-merge (lambda-closure f) caller-env)))
;; Too many args is an error; too few pads with nil
@@ -235,7 +235,7 @@
(define call-component
(fn (comp raw-args env)
(fn (comp (raw-args :as list) (env :as dict))
;; Parse keyword args and children from unevaluated arg list
(let ((parsed (parse-keyword-args raw-args env))
(kwargs (first parsed))
@@ -253,7 +253,7 @@
(define parse-keyword-args
(fn (raw-args env)
(fn ((raw-args :as list) (env :as dict))
;; Walk args: keyword + next-val → kwargs dict, else → children list
(let ((kwargs (dict))
(children (list))
@@ -287,7 +287,7 @@
;; --------------------------------------------------------------------------
(define sf-if
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((condition (trampoline (eval-expr (first args) env))))
(if (and condition (not (nil? condition)))
(make-thunk (nth args 1) env)
@@ -297,7 +297,7 @@
(define sf-when
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((condition (trampoline (eval-expr (first args) env))))
(if (and condition (not (nil? condition)))
(do
@@ -314,18 +314,18 @@
;; Checking only the first arg is ambiguous — (nil? x) is a 2-element
;; function call, not a scheme clause ((test body)).
(define cond-scheme?
(fn (clauses)
(fn ((clauses :as list))
(every? (fn (c) (and (= (type-of c) "list") (= (len c) 2)))
clauses)))
(define sf-cond
(fn (args env)
(fn ((args :as list) (env :as dict))
(if (cond-scheme? args)
(sf-cond-scheme args env)
(sf-cond-clojure args env))))
(define sf-cond-scheme
(fn (clauses env)
(fn ((clauses :as list) (env :as dict))
(if (empty? clauses)
nil
(let ((clause (first clauses))
@@ -342,7 +342,7 @@
(sf-cond-scheme (rest clauses) env)))))))
(define sf-cond-clojure
(fn (clauses env)
(fn ((clauses :as list) (env :as dict))
(if (< (len clauses) 2)
nil
(let ((test (first clauses))
@@ -358,13 +358,13 @@
(define sf-case
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((match-val (trampoline (eval-expr (first args) env)))
(clauses (rest args)))
(sf-case-loop match-val clauses env))))
(define sf-case-loop
(fn (match-val clauses env)
(fn (match-val (clauses :as list) (env :as dict))
(if (< (len clauses) 2)
nil
(let ((test (first clauses))
@@ -380,7 +380,7 @@
(define sf-and
(fn (args env)
(fn ((args :as list) (env :as dict))
(if (empty? args)
true
(let ((val (trampoline (eval-expr (first args) env))))
@@ -392,7 +392,7 @@
(define sf-or
(fn (args env)
(fn ((args :as list) (env :as dict))
(if (empty? args)
false
(let ((val (trampoline (eval-expr (first args) env))))
@@ -402,7 +402,7 @@
(define sf-let
(fn (args env)
(fn ((args :as list) (env :as dict))
;; Detect named let: (let name ((x 0) ...) body)
;; If first arg is a symbol, delegate to sf-named-let.
(if (= (type-of (first args)) "symbol")
@@ -443,7 +443,7 @@
;; Desugars to a self-recursive lambda called with initial values.
;; The loop name is bound in the body so recursive calls produce TCO thunks.
(define sf-named-let
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((loop-name (symbol-name (first args)))
(bindings (nth args 1))
(body (slice args 2))
@@ -483,22 +483,29 @@
(define sf-lambda
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((params-expr (first args))
(body-exprs (rest args))
(body (if (= (len body-exprs) 1)
(first body-exprs)
(cons (make-symbol "begin") body-exprs)))
(param-names (map (fn (p)
(if (= (type-of p) "symbol")
(symbol-name p)
p))
(cond
(= (type-of p) "symbol")
(symbol-name p)
;; Annotated param: (name :as type) → extract name
(and (= (type-of p) "list")
(= (len p) 3)
(= (type-of (nth p 1)) "keyword")
(= (keyword-name (nth p 1)) "as"))
(symbol-name (first p))
:else p))
params-expr)))
(make-lambda param-names body env))))
(define sf-define
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((name-sym (first args))
(value (trampoline (eval-expr (nth args 1) env))))
(when (and (lambda? value) (nil? (lambda-name value)))
@@ -508,7 +515,7 @@
(define sf-defcomp
(fn (args env)
(fn ((args :as list) (env :as dict))
;; (defcomp ~name (params) [:affinity :client|:server] body)
;; Body is always the last element. Optional keyword annotations
;; may appear between the params list and the body.
@@ -530,7 +537,7 @@
comp))))
(define defcomp-kwarg
(fn (args key default)
(fn ((args :as list) (key :as string) default)
;; Search for :key value between params (index 2) and body (last).
(let ((end (- (len args) 1))
(result default))
@@ -546,7 +553,7 @@
result)))
(define parse-comp-params
(fn (params-expr)
(fn ((params-expr :as list))
;; Parse (&key param1 param2 &children) → (params has-children param-types)
;; Also accepts &rest as synonym for &children.
;; Supports typed params: (name :as type) — a 3-element list where
@@ -588,7 +595,7 @@
(define sf-defisland
(fn (args env)
(fn ((args :as list) (env :as dict))
;; (defisland ~name (params) body)
;; Like defcomp but creates an island (reactive component).
;; Islands have the same calling convention as components but
@@ -606,7 +613,7 @@
(define sf-defmacro
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((name-sym (first args))
(params-raw (nth args 1))
(body (nth args 2))
@@ -618,7 +625,7 @@
mac))))
(define parse-macro-params
(fn (params-expr)
(fn ((params-expr :as list))
;; Parse (a b &rest rest) → ((a b) rest)
(let ((params (list))
(rest-param nil))
@@ -639,7 +646,7 @@
(define sf-defstyle
(fn (args env)
(fn ((args :as list) (env :as dict))
;; (defstyle name expr) — bind name to evaluated expr (string, function, etc.)
(let ((name-sym (first args))
(value (trampoline (eval-expr (nth args 1) env))))
@@ -648,7 +655,7 @@
(define sf-begin
(fn (args env)
(fn ((args :as list) (env :as dict))
(if (empty? args)
nil
(do
@@ -659,16 +666,16 @@
(define sf-quote
(fn (args env)
(fn ((args :as list) (env :as dict))
(if (empty? args) nil (first args))))
(define sf-quasiquote
(fn (args env)
(fn ((args :as list) (env :as dict))
(qq-expand (first args) env)))
(define qq-expand
(fn (template env)
(fn (template (env :as dict))
(if (not (= (type-of template) "list"))
template
(if (empty? template)
@@ -693,7 +700,7 @@
(define sf-thread-first
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((val (trampoline (eval-expr (first args) env))))
(reduce
(fn (result form)
@@ -720,7 +727,7 @@
(define sf-set!
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((name (symbol-name (first args)))
(value (trampoline (eval-expr (nth args 1) env))))
(env-set! env name value)
@@ -741,7 +748,7 @@
;; --------------------------------------------------------------------------
(define sf-letrec
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((bindings (first args))
(body (rest args))
(local (env-extend env))
@@ -816,7 +823,7 @@
;; --------------------------------------------------------------------------
(define sf-dynamic-wind
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((before (trampoline (eval-expr (first args) env)))
(body (trampoline (eval-expr (nth args 1) env)))
(after (trampoline (eval-expr (nth args 2) env))))
@@ -835,7 +842,7 @@
;; --------------------------------------------------------------------------
(define expand-macro
(fn (mac raw-args env)
(fn ((mac :as macro) (raw-args :as list) (env :as dict))
(let ((local (env-merge (macro-closure mac) env)))
;; Bind positional params (unevaluated)
(for-each
@@ -859,20 +866,20 @@
;; call-fn: unified caller for HO forms — handles both Lambda and native callable
(define call-fn
(fn (f args env)
(fn (f (args :as list) (env :as dict))
(cond
(lambda? f) (trampoline (call-lambda f args env))
(callable? f) (apply f args)
:else (error (str "Not callable in HO form: " (inspect f))))))
(define ho-map
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(map (fn (item) (call-fn f (list item) env)) coll))))
(define ho-map-indexed
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(map-indexed
@@ -880,7 +887,7 @@
coll))))
(define ho-filter
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(filter
@@ -888,7 +895,7 @@
coll))))
(define ho-reduce
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((f (trampoline (eval-expr (first args) env)))
(init (trampoline (eval-expr (nth args 1) env)))
(coll (trampoline (eval-expr (nth args 2) env))))
@@ -898,7 +905,7 @@
coll))))
(define ho-some
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(some
@@ -906,7 +913,7 @@
coll))))
(define ho-every
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(every?
@@ -915,7 +922,7 @@
(define ho-for-each
(fn (args env)
(fn ((args :as list) (env :as dict))
(let ((f (trampoline (eval-expr (first args) env)))
(coll (trampoline (eval-expr (nth args 1) env))))
(for-each