Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 57s
lib/minikanren/stream.sx: mzero/unit/mk-mplus/mk-bind/stream-take. Three stream shapes (empty, mature list, immature thunk). mk-mplus suspends and swaps on a paused-left for fair interleaving (Reasoned Schemer style). lib/minikanren/goals.sx: succeed/fail/==/==-check + conj2/disj2 + variadic mk-conj/mk-disj. ==-check is the opt-in occurs-checked variant. Forced-rename note: SX has a host primitive `bind` that silently shadows user-level defines, so all stream/goal operators are mk-prefixed. Recorded in feedback memory. 82/82 tests cumulative (48 unify + 34 goals).
59 lines
1.8 KiB
Plaintext
59 lines
1.8 KiB
Plaintext
;; lib/minikanren/stream.sx — Phase 2 piece A: lazy streams of substitutions.
|
|
;;
|
|
;; Three stream shapes per The Reasoned Schemer (chapter 9):
|
|
;; mzero — empty stream (the SX empty list)
|
|
;; mature — '(s . rest) (a proper SX list of substitutions)
|
|
;; immature — a thunk (an SX lambda taking zero args, returns a stream)
|
|
;;
|
|
;; Immature thunks are how miniKanren keeps search lazy and supports
|
|
;; interleaved disjunction without diverging on left-recursive relations.
|
|
;; SX has plain function closures, so a thunk is just (fn () body).
|
|
;;
|
|
;; Names are mk-prefixed: SX has a host primitive `bind` that would shadow
|
|
;; a user-level definition.
|
|
|
|
(define mzero (list))
|
|
|
|
(define unit (fn (s) (list s)))
|
|
|
|
(define stream-pause? (fn (s) (and (not (list? s)) (callable? s))))
|
|
|
|
;; mk-mplus — interleave two streams. If the first is mature/empty we use it
|
|
;; directly; if it is paused, we suspend and swap so the other stream gets
|
|
;; explored fairly (Reasoned Schemer "interleave").
|
|
(define
|
|
mk-mplus
|
|
(fn
|
|
(s1 s2)
|
|
(cond
|
|
((empty? s1) s2)
|
|
((stream-pause? s1) (fn () (mk-mplus s2 (s1))))
|
|
(:else (cons (first s1) (mk-mplus (rest s1) s2))))))
|
|
|
|
;; mk-bind — apply goal g to every substitution in stream s, mk-mplus-ing results.
|
|
(define
|
|
mk-bind
|
|
(fn
|
|
(s g)
|
|
(cond
|
|
((empty? s) mzero)
|
|
((stream-pause? s) (fn () (mk-bind (s) g)))
|
|
(:else (mk-mplus (g (first s)) (mk-bind (rest s) g))))))
|
|
|
|
;; stream-take — force up to n results out of a (possibly lazy) stream.
|
|
;; n = -1 to take all (used by run*).
|
|
(define
|
|
stream-take
|
|
(fn
|
|
(n s)
|
|
(cond
|
|
((= n 0) (list))
|
|
((empty? s) (list))
|
|
((stream-pause? s) (stream-take n (s)))
|
|
(:else
|
|
(cons
|
|
(first s)
|
|
(stream-take
|
|
(if (= n -1) -1 (- n 1))
|
|
(rest s)))))))
|