spec: promises — delay/force/delay-force/make-promise/promise?

25 tests pass on OCaml (4357 total) and JS. Promise represented as
mutable dict {:_promise true :forced :thunk :value}; delay-force
adds :_iterative for chain-following semantics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-01 08:21:45 +00:00
parent 835b5314ce
commit e44cb89ab4
5 changed files with 306 additions and 3 deletions

View File

@@ -2037,8 +2037,10 @@
nil))))
(define
values
(fn (&rest vs) (if (= (len vs) 1) (first vs) {:_values true :_list vs})))
sf-delay
(fn
(args env)
(let ((thunk (make-lambda (list) (first args) env))) {:forced false :value nil :thunk thunk :_promise true})))
;; ═══════════════════════════════════════════════════════════════
;; Part 11: Entry Points
@@ -2047,6 +2049,42 @@
;; eval-expr / trampoline: top-level bindings that override the
;; forward declarations from Part 5.
;; ═══════════════════════════════════════════════════════════════
(define
sf-delay-force
(fn
(args env)
(let ((thunk (make-lambda (list) (first args) env))) {:_iterative true :forced false :value nil :thunk thunk :_promise true})))
(define promise? (fn (v) (and (dict? v) (get v :_promise false))))
(define make-promise (fn (v) {:forced true :value v :_promise true}))
(define
force
(fn
(p)
(if
(not (promise? p))
p
(if
(get p :forced false)
(get p :value nil)
(let
((result (apply (get p :thunk nil) (list))))
(let
((final (if (and (get p :_iterative false) (promise? result)) (force result) result)))
(dict-set! p :forced true)
(dict-set! p :value final)
final))))))
(register-special-form! "delay" sf-delay)
(register-special-form! "delay-force" sf-delay-force)
(define
values
(fn (&rest vs) (if (= (len vs) 1) (first vs) {:_values true :_list vs})))
(register-special-form! "define-type" sf-define-type)
(define

150
spec/tests/test-promises.sx Normal file
View File

@@ -0,0 +1,150 @@
(defsuite
"promises"
(deftest
"delay creates a promise"
(do (assert (promise? (delay 42)))))
(deftest
"delay does not evaluate immediately"
(do
(let
((count 0))
(let
((p (delay (do (set! count (+ count 1)) count))))
(assert= 0 count)))))
(deftest
"force evaluates the expression"
(do (assert= 42 (force (delay 42)))))
(deftest
"force with arithmetic"
(do (assert= 10 (force (delay (+ 3 7))))))
(deftest
"force memoises result"
(do
(let
((count 0))
(let
((p (delay (do (set! count (+ count 1)) count))))
(force p)
(force p)
(assert= 1 count)))))
(deftest
"force returns same value on repeated calls"
(do
(let
((p (delay (+ 1 2))))
(assert= 3 (force p))
(assert= 3 (force p)))))
(deftest
"make-promise creates an already-forced promise"
(do
(let
((p (make-promise 99)))
(assert (promise? p))
(assert= 99 (force p)))))
(deftest
"make-promise memoises without evaluating"
(do
(let
((count 0))
(let
((p (make-promise 42)))
(force p)
(force p)
(assert= 0 count)))))
(deftest
"promise? returns true for delay"
(do (assert (promise? (delay 1)))))
(deftest
"promise? returns true for make-promise"
(do (assert (promise? (make-promise 1)))))
(deftest
"promise? returns false for non-promise"
(do
(assert= false (promise? 42))
(assert= false (promise? "hello"))
(assert= false (promise? nil))
(assert= false (promise? (list 1 2)))))
(deftest
"force non-promise returns value unchanged"
(do
(assert= 42 (force 42))
(assert= "hi" (force "hi"))
(assert= nil (force nil))))
(deftest
"delay captures environment"
(do
(let
((x 10))
(let
((p (delay (+ x 5))))
(assert= 15 (force p))))))
(deftest
"delay-force basic"
(do (assert= 42 (force (delay-force (delay 42))))))
(deftest
"delay-force chains"
(do
(assert=
5
(force (delay-force (delay-force (delay 5)))))))
(deftest
"delay with string"
(do (assert= "hello" (force (delay "hello")))))
(deftest
"delay with list"
(do
(assert-equal
(list 1 2 3)
(force (delay (list 1 2 3))))))
(deftest
"delay with function call"
(do (assert= 6 (force (delay (* 2 3))))))
(deftest
"nested delay"
(do
(let
((p (delay (delay 99))))
(assert (promise? (force p))))))
(deftest
"force already forced promise"
(do
(let
((p (make-promise 7)))
(assert= 7 (force p))
(assert= 7 (force p)))))
(deftest
"lazy stream first element"
(do
(define (stream-cons x s) (delay (list x s)))
(define (stream-car s) (first (force s)))
(define (stream-cdr s) (nth (force s) 1))
(let
((s (stream-cons 1 (stream-cons 2 (stream-cons 3 nil)))))
(assert= 1 (stream-car s))
(assert= 2 (stream-car (stream-cdr s))))))
(deftest
"delay-force is a promise"
(do (assert (promise? (delay-force (delay 1))))))
(deftest
"force with side effects runs once"
(do
(let
((log (list)))
(let
((p (delay (do (set! log (cons 42 log)) 42))))
(force p)
(force p)
(assert= 1 (len log))))))
(deftest
"make-promise with nil"
(do
(let
((p (make-promise nil)))
(assert (promise? p))
(assert= nil (force p)))))
(deftest
"delay in let binding"
(do
(let
((p (delay (+ 10 20))))
(assert= 30 (force p))))))