;; 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))))
