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))))))))
|
||||
|
||||
|
||||
|
||||
@@ -112,10 +112,14 @@
|
||||
(fn (remaining env)
|
||||
{:type "thread" :remaining remaining :env env}))
|
||||
|
||||
;; MapFrame: higher-order map in progress
|
||||
;; MapFrame: higher-order map/map-indexed in progress
|
||||
(define make-map-frame
|
||||
(fn (f remaining results env)
|
||||
{:type "map" :f f :remaining remaining :results results :env env}))
|
||||
{:type "map" :f f :remaining remaining :results results :env env :indexed false}))
|
||||
|
||||
(define make-map-indexed-frame
|
||||
(fn (f remaining results env)
|
||||
{:type "map" :f f :remaining remaining :results results :env env :indexed true}))
|
||||
|
||||
;; FilterFrame: higher-order filter in progress
|
||||
(define make-filter-frame
|
||||
@@ -133,6 +137,16 @@
|
||||
(fn (f remaining env)
|
||||
{:type "for-each" :f f :remaining remaining :env env}))
|
||||
|
||||
;; SomeFrame: higher-order some (short-circuit on first truthy)
|
||||
(define make-some-frame
|
||||
(fn (f remaining env)
|
||||
{:type "some" :f f :remaining remaining :env env}))
|
||||
|
||||
;; EveryFrame: higher-order every? (short-circuit on first falsy)
|
||||
(define make-every-frame
|
||||
(fn (f remaining env)
|
||||
{:type "every" :f f :remaining remaining :env env}))
|
||||
|
||||
;; ScopeFrame: scope-pop! when frame pops
|
||||
(define make-scope-frame
|
||||
(fn (name remaining env)
|
||||
|
||||
@@ -3256,7 +3256,11 @@ def make_thread_frame(remaining, env):
|
||||
|
||||
# make-map-frame
|
||||
def make_map_frame(f, remaining, results, env):
|
||||
return {'type': 'map', 'f': f, 'remaining': remaining, 'results': results, 'env': env}
|
||||
return {'type': 'map', 'f': f, 'remaining': remaining, 'results': results, 'env': env, 'indexed': False}
|
||||
|
||||
# make-map-indexed-frame
|
||||
def make_map_indexed_frame(f, remaining, results, env):
|
||||
return {'type': 'map', 'f': f, 'remaining': remaining, 'results': results, 'env': env, 'indexed': True}
|
||||
|
||||
# make-filter-frame
|
||||
def make_filter_frame(f, remaining, results, current_item, env):
|
||||
@@ -3270,6 +3274,14 @@ def make_reduce_frame(f, remaining, env):
|
||||
def make_for_each_frame(f, remaining, env):
|
||||
return {'type': 'for-each', 'f': f, 'remaining': remaining, 'env': env}
|
||||
|
||||
# make-some-frame
|
||||
def make_some_frame(f, remaining, env):
|
||||
return {'type': 'some', 'f': f, 'remaining': remaining, 'env': env}
|
||||
|
||||
# make-every-frame
|
||||
def make_every_frame(f, remaining, env):
|
||||
return {'type': 'every', 'f': f, 'remaining': remaining, 'env': env}
|
||||
|
||||
# make-scope-frame
|
||||
def make_scope_frame(name, remaining, env):
|
||||
return {'type': 'scope', 'name': name, 'remaining': remaining, 'env': env}
|
||||
@@ -3994,15 +4006,15 @@ def step_eval_list(expr, env, kont):
|
||||
elif sx_truthy((name == 'map')):
|
||||
return step_ho_map(args, env, kont)
|
||||
elif sx_truthy((name == 'map-indexed')):
|
||||
return make_cek_value(ho_map_indexed(args, env), env, kont)
|
||||
return step_ho_map_indexed(args, env, kont)
|
||||
elif sx_truthy((name == 'filter')):
|
||||
return step_ho_filter(args, env, kont)
|
||||
elif sx_truthy((name == 'reduce')):
|
||||
return step_ho_reduce(args, env, kont)
|
||||
elif sx_truthy((name == 'some')):
|
||||
return make_cek_value(ho_some(args, env), env, kont)
|
||||
return step_ho_some(args, env, kont)
|
||||
elif sx_truthy((name == 'every?')):
|
||||
return make_cek_value(ho_every(args, env), env, kont)
|
||||
return step_ho_every(args, env, kont)
|
||||
elif sx_truthy((name == 'for-each')):
|
||||
return step_ho_for_each(args, env, kont)
|
||||
elif sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))):
|
||||
@@ -4178,19 +4190,67 @@ def step_eval_call(head, args, env, kont):
|
||||
|
||||
# step-ho-map
|
||||
def step_ho_map(args, env, kont):
|
||||
return make_cek_value(ho_map(args, env), env, kont)
|
||||
f = trampoline(eval_expr(first(args), env))
|
||||
coll = trampoline(eval_expr(nth(args, 1), env))
|
||||
if sx_truthy(empty_p(coll)):
|
||||
return make_cek_value([], env, kont)
|
||||
else:
|
||||
return continue_with_call(f, [first(coll)], env, [], kont_push(make_map_frame(f, rest(coll), [], env), kont))
|
||||
|
||||
# step-ho-map-indexed
|
||||
def step_ho_map_indexed(args, env, kont):
|
||||
f = trampoline(eval_expr(first(args), env))
|
||||
coll = trampoline(eval_expr(nth(args, 1), env))
|
||||
if sx_truthy(empty_p(coll)):
|
||||
return make_cek_value([], env, kont)
|
||||
else:
|
||||
return continue_with_call(f, [0, first(coll)], env, [], kont_push(make_map_indexed_frame(f, rest(coll), [], env), kont))
|
||||
|
||||
# step-ho-filter
|
||||
def step_ho_filter(args, env, kont):
|
||||
return make_cek_value(ho_filter(args, env), env, kont)
|
||||
f = trampoline(eval_expr(first(args), env))
|
||||
coll = trampoline(eval_expr(nth(args, 1), env))
|
||||
if sx_truthy(empty_p(coll)):
|
||||
return make_cek_value([], env, kont)
|
||||
else:
|
||||
return continue_with_call(f, [first(coll)], env, [], kont_push(make_filter_frame(f, rest(coll), [], first(coll), env), kont))
|
||||
|
||||
# step-ho-reduce
|
||||
def step_ho_reduce(args, env, kont):
|
||||
return make_cek_value(ho_reduce(args, env), env, kont)
|
||||
f = trampoline(eval_expr(first(args), env))
|
||||
init = trampoline(eval_expr(nth(args, 1), env))
|
||||
coll = trampoline(eval_expr(nth(args, 2), env))
|
||||
if sx_truthy(empty_p(coll)):
|
||||
return make_cek_value(init, env, kont)
|
||||
else:
|
||||
return continue_with_call(f, [init, first(coll)], env, [], kont_push(make_reduce_frame(f, rest(coll), env), kont))
|
||||
|
||||
# step-ho-some
|
||||
def step_ho_some(args, env, kont):
|
||||
f = trampoline(eval_expr(first(args), env))
|
||||
coll = trampoline(eval_expr(nth(args, 1), env))
|
||||
if sx_truthy(empty_p(coll)):
|
||||
return make_cek_value(False, env, kont)
|
||||
else:
|
||||
return continue_with_call(f, [first(coll)], env, [], kont_push(make_some_frame(f, rest(coll), env), kont))
|
||||
|
||||
# step-ho-every
|
||||
def step_ho_every(args, env, kont):
|
||||
f = trampoline(eval_expr(first(args), env))
|
||||
coll = trampoline(eval_expr(nth(args, 1), env))
|
||||
if sx_truthy(empty_p(coll)):
|
||||
return make_cek_value(True, env, kont)
|
||||
else:
|
||||
return continue_with_call(f, [first(coll)], env, [], kont_push(make_every_frame(f, rest(coll), env), kont))
|
||||
|
||||
# step-ho-for-each
|
||||
def step_ho_for_each(args, env, kont):
|
||||
return make_cek_value(ho_for_each(args, env), env, kont)
|
||||
f = trampoline(eval_expr(first(args), env))
|
||||
coll = trampoline(eval_expr(nth(args, 1), env))
|
||||
if sx_truthy(empty_p(coll)):
|
||||
return make_cek_value(NIL, env, kont)
|
||||
else:
|
||||
return continue_with_call(f, [first(coll)], env, [], kont_push(make_for_each_frame(f, rest(coll), env), kont))
|
||||
|
||||
# step-continue
|
||||
def step_continue(state):
|
||||
@@ -4400,6 +4460,68 @@ def step_continue(state):
|
||||
return make_cek_value(value, fenv, rest_k)
|
||||
else:
|
||||
return make_cek_state(first(remaining), fenv, kont_push(make_scope_frame(name, rest(remaining), fenv), rest_k))
|
||||
elif sx_truthy((ft == 'map')):
|
||||
f = get(frame, 'f')
|
||||
remaining = get(frame, 'remaining')
|
||||
results = get(frame, 'results')
|
||||
indexed = get(frame, 'indexed')
|
||||
fenv = get(frame, 'env')
|
||||
new_results = append(results, [value])
|
||||
if sx_truthy(empty_p(remaining)):
|
||||
return make_cek_value(new_results, fenv, rest_k)
|
||||
else:
|
||||
call_args = ([len(new_results), first(remaining)] if sx_truthy(indexed) else [first(remaining)])
|
||||
next_frame = (make_map_indexed_frame(f, rest(remaining), new_results, fenv) if sx_truthy(indexed) else make_map_frame(f, rest(remaining), new_results, fenv))
|
||||
return continue_with_call(f, call_args, fenv, [], kont_push(next_frame, rest_k))
|
||||
elif sx_truthy((ft == 'filter')):
|
||||
f = get(frame, 'f')
|
||||
remaining = get(frame, 'remaining')
|
||||
results = get(frame, 'results')
|
||||
current_item = get(frame, 'current-item')
|
||||
fenv = get(frame, 'env')
|
||||
new_results = (append(results, [current_item]) if sx_truthy(value) else results)
|
||||
if sx_truthy(empty_p(remaining)):
|
||||
return make_cek_value(new_results, fenv, rest_k)
|
||||
else:
|
||||
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_filter_frame(f, rest(remaining), new_results, first(remaining), fenv), rest_k))
|
||||
elif sx_truthy((ft == 'reduce')):
|
||||
f = get(frame, 'f')
|
||||
remaining = get(frame, 'remaining')
|
||||
fenv = get(frame, 'env')
|
||||
if sx_truthy(empty_p(remaining)):
|
||||
return make_cek_value(value, fenv, rest_k)
|
||||
else:
|
||||
return continue_with_call(f, [value, first(remaining)], fenv, [], kont_push(make_reduce_frame(f, rest(remaining), fenv), rest_k))
|
||||
elif sx_truthy((ft == 'for-each')):
|
||||
f = get(frame, 'f')
|
||||
remaining = get(frame, 'remaining')
|
||||
fenv = get(frame, 'env')
|
||||
if sx_truthy(empty_p(remaining)):
|
||||
return make_cek_value(NIL, fenv, rest_k)
|
||||
else:
|
||||
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_for_each_frame(f, rest(remaining), fenv), rest_k))
|
||||
elif sx_truthy((ft == 'some')):
|
||||
f = get(frame, 'f')
|
||||
remaining = get(frame, 'remaining')
|
||||
fenv = get(frame, 'env')
|
||||
if sx_truthy(value):
|
||||
return make_cek_value(value, fenv, rest_k)
|
||||
else:
|
||||
if sx_truthy(empty_p(remaining)):
|
||||
return make_cek_value(False, fenv, rest_k)
|
||||
else:
|
||||
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_some_frame(f, rest(remaining), fenv), rest_k))
|
||||
elif sx_truthy((ft == 'every')):
|
||||
f = get(frame, 'f')
|
||||
remaining = get(frame, 'remaining')
|
||||
fenv = get(frame, 'env')
|
||||
if sx_truthy((not sx_truthy(value))):
|
||||
return make_cek_value(False, fenv, rest_k)
|
||||
else:
|
||||
if sx_truthy(empty_p(remaining)):
|
||||
return make_cek_value(True, fenv, rest_k)
|
||||
else:
|
||||
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_every_frame(f, rest(remaining), fenv), rest_k))
|
||||
else:
|
||||
return error(sx_str('Unknown frame type: ', ft))
|
||||
|
||||
|
||||
@@ -209,3 +209,71 @@
|
||||
(reset! s 3)))
|
||||
;; batch should coalesce — effect runs once, not three times
|
||||
(assert-equal 2 (deref count)))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; CEK-native higher-order forms
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defsuite "CEK higher-order forms"
|
||||
(deftest "map through CEK"
|
||||
(let ((result (eval-expr-cek
|
||||
(sx-parse-one "(map (fn (x) (* x 2)) (list 1 2 3))")
|
||||
(test-env))))
|
||||
(assert-equal (list 2 4 6) result)))
|
||||
|
||||
(deftest "map-indexed through CEK"
|
||||
(let ((result (eval-expr-cek
|
||||
(sx-parse-one "(map-indexed (fn (i x) (+ i x)) (list 10 20 30))")
|
||||
(test-env))))
|
||||
(assert-equal (list 10 21 32) result)))
|
||||
|
||||
(deftest "filter through CEK"
|
||||
(let ((result (eval-expr-cek
|
||||
(sx-parse-one "(filter (fn (x) (> x 2)) (list 1 2 3 4 5))")
|
||||
(test-env))))
|
||||
(assert-equal (list 3 4 5) result)))
|
||||
|
||||
(deftest "reduce through CEK"
|
||||
(let ((result (eval-expr-cek
|
||||
(sx-parse-one "(reduce (fn (acc x) (+ acc x)) 0 (list 1 2 3))")
|
||||
(test-env))))
|
||||
(assert-equal 6 result)))
|
||||
|
||||
(deftest "some through CEK"
|
||||
(let ((result (eval-expr-cek
|
||||
(sx-parse-one "(some (fn (x) (> x 3)) (list 1 2 3 4 5))")
|
||||
(test-env))))
|
||||
(assert-true result)))
|
||||
|
||||
(deftest "some returns false when none match"
|
||||
(let ((result (eval-expr-cek
|
||||
(sx-parse-one "(some (fn (x) (> x 10)) (list 1 2 3))")
|
||||
(test-env))))
|
||||
(assert-false result)))
|
||||
|
||||
(deftest "every? through CEK"
|
||||
(let ((result (eval-expr-cek
|
||||
(sx-parse-one "(every? (fn (x) (> x 0)) (list 1 2 3))")
|
||||
(test-env))))
|
||||
(assert-true result)))
|
||||
|
||||
(deftest "every? returns false on first falsy"
|
||||
(let ((result (eval-expr-cek
|
||||
(sx-parse-one "(every? (fn (x) (> x 2)) (list 1 2 3))")
|
||||
(test-env))))
|
||||
(assert-false result)))
|
||||
|
||||
(deftest "for-each through CEK"
|
||||
(let ((log (list)))
|
||||
(env-set! (test-env) "test-log" log)
|
||||
(eval-expr-cek
|
||||
(sx-parse-one "(for-each (fn (x) (append! test-log x)) (list 1 2 3))")
|
||||
(test-env))
|
||||
(assert-equal (list 1 2 3) log)))
|
||||
|
||||
(deftest "map on empty list"
|
||||
(let ((result (eval-expr-cek
|
||||
(sx-parse-one "(map (fn (x) x) (list))")
|
||||
(test-env))))
|
||||
(assert-equal (list) result))))
|
||||
|
||||
Reference in New Issue
Block a user