;; lib/minikanren/condu.sx — Phase 2 piece D: `condu` and `onceo`. ;; ;; Both are commitment forms (no backtracking into discarded options): ;; ;; (onceo g) — succeeds at most once: takes the first answer ;; stream-take produces from (g s). ;; ;; (condu (g0 g ...) (h0 h ...) ...) ;; — first clause whose head goal succeeds wins; only ;; the first answer of the head is propagated to the ;; rest of that clause; later clauses are not tried. ;; (Reasoned Schemer chapter 10; Byrd 5.4.) (define onceo (fn (g) (fn (s) (let ((peek (stream-take 1 (g s)))) (if (empty? peek) mzero (unit (first peek))))))) ;; condu-try — runtime walker over a list of clauses (each clause a list of ;; goals). Forces the head with stream-take 1; if head fails, recurse to ;; the next clause; if head succeeds, commits its single answer through ;; the rest of the clause. (define condu-try (fn (clauses s) (cond ((empty? clauses) mzero) (:else (let ((cl (first clauses))) (let ((head-goal (first cl)) (rest-goals (rest cl))) (let ((peek (stream-take 1 (head-goal s)))) (if (empty? peek) (condu-try (rest clauses) s) ((mk-conj-list rest-goals) (first peek)))))))))) (defmacro condu (&rest clauses) (quasiquote (fn (s) (condu-try (list (splice-unquote (map (fn (cl) (quasiquote (list (splice-unquote cl)))) clauses))) s))))