mk: phase 2A — streams + ==/conj/disj, 34 new tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 57s
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).
This commit is contained in:
58
lib/minikanren/stream.sx
Normal file
58
lib/minikanren/stream.sx
Normal file
@@ -0,0 +1,58 @@
|
||||
;; 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)))))))
|
||||
Reference in New Issue
Block a user