CEK-native higher-order forms: map, filter, reduce, some, every?, for-each
Some checks are pending
Build and Deploy / build-and-deploy (push) Has started running
Some checks are pending
Build and Deploy / build-and-deploy (push) Has started running
Higher-order forms now step element-by-element through the CEK machine using dedicated frames instead of delegating to tree-walk ho-map etc. Each callback invocation goes through continue-with-call, so deref-as-shift works inside map/filter/reduce callbacks in reactive island contexts. - cek.sx: rewrite step-ho-* to use CEK frames, add frame handlers in step-continue for map, filter, reduce, for-each, some, every - frames.sx: add SomeFrame, EveryFrame, MapIndexedFrame - test-cek-reactive.sx: add 10 tests for CEK-native HO forms 89 tests pass (20 signal + 43 CEK + 26 CEK reactive). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -168,11 +168,11 @@
|
||||
|
||||
;; Higher-order forms
|
||||
(= name "map") (step-ho-map args env kont)
|
||||
(= name "map-indexed") (make-cek-value (ho-map-indexed args env) env kont)
|
||||
(= name "map-indexed") (step-ho-map-indexed args env kont)
|
||||
(= name "filter") (step-ho-filter args env kont)
|
||||
(= name "reduce") (step-ho-reduce args env kont)
|
||||
(= name "some") (make-cek-value (ho-some args env) env kont)
|
||||
(= name "every?") (make-cek-value (ho-every args env) env kont)
|
||||
(= name "some") (step-ho-some args env kont)
|
||||
(= name "every?") (step-ho-every args env kont)
|
||||
(= name "for-each") (step-ho-for-each args env kont)
|
||||
|
||||
;; Macro expansion
|
||||
@@ -477,23 +477,74 @@
|
||||
;; 7. Higher-order form step handlers
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
;; CEK-native higher-order forms — each callback invocation goes through
|
||||
;; continue-with-call so deref-as-shift works inside callbacks.
|
||||
;; Function and collection args are evaluated via tree-walk (simple exprs),
|
||||
;; then the loop is driven by CEK frames.
|
||||
|
||||
(define step-ho-map
|
||||
(fn (args env kont)
|
||||
;; Evaluate function, then collection
|
||||
;; For now, delegate to existing ho-map (it's a tight loop)
|
||||
(make-cek-value (ho-map args env) env kont)))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(if (empty? coll)
|
||||
(make-cek-value (list) env kont)
|
||||
(continue-with-call f (list (first coll)) env (list)
|
||||
(kont-push (make-map-frame f (rest coll) (list) env) kont))))))
|
||||
|
||||
(define step-ho-map-indexed
|
||||
(fn (args env kont)
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(if (empty? coll)
|
||||
(make-cek-value (list) env kont)
|
||||
(continue-with-call f (list 0 (first coll)) env (list)
|
||||
(kont-push (make-map-indexed-frame f (rest coll) (list) env) kont))))))
|
||||
|
||||
(define step-ho-filter
|
||||
(fn (args env kont)
|
||||
(make-cek-value (ho-filter args env) env kont)))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(if (empty? coll)
|
||||
(make-cek-value (list) env kont)
|
||||
(continue-with-call f (list (first coll)) env (list)
|
||||
(kont-push (make-filter-frame f (rest coll) (list) (first coll) env) kont))))))
|
||||
|
||||
(define step-ho-reduce
|
||||
(fn (args env kont)
|
||||
(make-cek-value (ho-reduce args env) env kont)))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(init (trampoline (eval-expr (nth args 1) env)))
|
||||
(coll (trampoline (eval-expr (nth args 2) env))))
|
||||
(if (empty? coll)
|
||||
(make-cek-value init env kont)
|
||||
(continue-with-call f (list init (first coll)) env (list)
|
||||
(kont-push (make-reduce-frame f (rest coll) env) kont))))))
|
||||
|
||||
(define step-ho-some
|
||||
(fn (args env kont)
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(if (empty? coll)
|
||||
(make-cek-value false env kont)
|
||||
(continue-with-call f (list (first coll)) env (list)
|
||||
(kont-push (make-some-frame f (rest coll) env) kont))))))
|
||||
|
||||
(define step-ho-every
|
||||
(fn (args env kont)
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(if (empty? coll)
|
||||
(make-cek-value true env kont)
|
||||
(continue-with-call f (list (first coll)) env (list)
|
||||
(kont-push (make-every-frame f (rest coll) env) kont))))))
|
||||
|
||||
(define step-ho-for-each
|
||||
(fn (args env kont)
|
||||
(make-cek-value (ho-for-each args env) env kont)))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(if (empty? coll)
|
||||
(make-cek-value nil env kont)
|
||||
(continue-with-call f (list (first coll)) env (list)
|
||||
(kont-push (make-for-each-frame f (rest coll) env) kont))))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
@@ -809,6 +860,84 @@
|
||||
(make-scope-frame name (rest remaining) fenv)
|
||||
rest-k))))
|
||||
|
||||
;; --- MapFrame: callback result for map/map-indexed ---
|
||||
(= ft "map")
|
||||
(let ((f (get frame "f"))
|
||||
(remaining (get frame "remaining"))
|
||||
(results (get frame "results"))
|
||||
(indexed (get frame "indexed"))
|
||||
(fenv (get frame "env")))
|
||||
(let ((new-results (append results (list value))))
|
||||
(if (empty? remaining)
|
||||
(make-cek-value new-results fenv rest-k)
|
||||
(let ((call-args (if indexed
|
||||
(list (len new-results) (first remaining))
|
||||
(list (first remaining))))
|
||||
(next-frame (if indexed
|
||||
(make-map-indexed-frame f (rest remaining) new-results fenv)
|
||||
(make-map-frame f (rest remaining) new-results fenv))))
|
||||
(continue-with-call f call-args fenv (list)
|
||||
(kont-push next-frame rest-k))))))
|
||||
|
||||
;; --- FilterFrame: predicate result ---
|
||||
(= ft "filter")
|
||||
(let ((f (get frame "f"))
|
||||
(remaining (get frame "remaining"))
|
||||
(results (get frame "results"))
|
||||
(current-item (get frame "current-item"))
|
||||
(fenv (get frame "env")))
|
||||
(let ((new-results (if value
|
||||
(append results (list current-item))
|
||||
results)))
|
||||
(if (empty? remaining)
|
||||
(make-cek-value new-results fenv rest-k)
|
||||
(continue-with-call f (list (first remaining)) fenv (list)
|
||||
(kont-push (make-filter-frame f (rest remaining) new-results (first remaining) fenv) rest-k)))))
|
||||
|
||||
;; --- ReduceFrame: accumulator step ---
|
||||
(= ft "reduce")
|
||||
(let ((f (get frame "f"))
|
||||
(remaining (get frame "remaining"))
|
||||
(fenv (get frame "env")))
|
||||
(if (empty? remaining)
|
||||
(make-cek-value value fenv rest-k)
|
||||
(continue-with-call f (list value (first remaining)) fenv (list)
|
||||
(kont-push (make-reduce-frame f (rest remaining) fenv) rest-k))))
|
||||
|
||||
;; --- ForEachFrame: side effect, discard result ---
|
||||
(= ft "for-each")
|
||||
(let ((f (get frame "f"))
|
||||
(remaining (get frame "remaining"))
|
||||
(fenv (get frame "env")))
|
||||
(if (empty? remaining)
|
||||
(make-cek-value nil fenv rest-k)
|
||||
(continue-with-call f (list (first remaining)) fenv (list)
|
||||
(kont-push (make-for-each-frame f (rest remaining) fenv) rest-k))))
|
||||
|
||||
;; --- SomeFrame: short-circuit on first truthy ---
|
||||
(= ft "some")
|
||||
(let ((f (get frame "f"))
|
||||
(remaining (get frame "remaining"))
|
||||
(fenv (get frame "env")))
|
||||
(if value
|
||||
(make-cek-value value fenv rest-k)
|
||||
(if (empty? remaining)
|
||||
(make-cek-value false fenv rest-k)
|
||||
(continue-with-call f (list (first remaining)) fenv (list)
|
||||
(kont-push (make-some-frame f (rest remaining) fenv) rest-k)))))
|
||||
|
||||
;; --- EveryFrame: short-circuit on first falsy ---
|
||||
(= ft "every")
|
||||
(let ((f (get frame "f"))
|
||||
(remaining (get frame "remaining"))
|
||||
(fenv (get frame "env")))
|
||||
(if (not value)
|
||||
(make-cek-value false fenv rest-k)
|
||||
(if (empty? remaining)
|
||||
(make-cek-value true fenv rest-k)
|
||||
(continue-with-call f (list (first remaining)) fenv (list)
|
||||
(kont-push (make-every-frame f (rest remaining) fenv) rest-k)))))
|
||||
|
||||
:else (error (str "Unknown frame type: " ft))))))))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user