;; lib/minikanren/tests/conde.sx — Phase 2 piece C tests for `conde`. ;; ;; Note on ordering: conde clauses are wrapped in Zzz (inverse-eta delay), ;; so applying the conde goal to a substitution returns thunks. mk-mplus ;; suspends-and-swaps when its left operand is paused, giving fair ;; interleaving — this is exactly what makes recursive relations work, ;; but it does mean conde answers can interleave rather than appear in ;; strict left-to-right clause order. ;; --- single-clause conde ≡ conj of clause body --- (mk-test "conde-one-clause" (let ((q (mk-var "q"))) (run* q (conde ((== q 7))))) (list 7)) (mk-test "conde-one-clause-multi-goals" (let ((q (mk-var "q"))) (run* q (conde ((fresh (x) (== x 5) (== q (list x x))))))) (list (list 5 5))) ;; --- multi-clause: produces one row per clause (interleaved) --- (mk-test "conde-three-clauses-as-set" (let ((qs (run* q (conde ((== q 1)) ((== q 2)) ((== q 3)))))) (and (= (len qs) 3) (and (some (fn (x) (= x 1)) qs) (and (some (fn (x) (= x 2)) qs) (some (fn (x) (= x 3)) qs))))) true) (mk-test "conde-mixed-success-failure-as-set" (let ((qs (run* q (conde ((== q "a")) ((== 1 2)) ((== q "b")))))) (and (= (len qs) 2) (and (some (fn (x) (= x "a")) qs) (some (fn (x) (= x "b")) qs)))) true) ;; --- conde with conjuncts inside clauses --- (mk-test "conde-clause-conj-as-set" (let ((rows (run* q (fresh (x y) (conde ((== x 1) (== y 10)) ((== x 2) (== y 20))) (== q (list x y)))))) (and (= (len rows) 2) (and (some (fn (r) (= r (list 1 10))) rows) (some (fn (r) (= r (list 2 20))) rows)))) true) ;; --- nested conde --- (mk-test "conde-nested-yields-three" (let ((qs (run* q (conde ((conde ((== q 1)) ((== q 2)))) ((== q 3)))))) (and (= (len qs) 3) (and (some (fn (x) (= x 1)) qs) (and (some (fn (x) (= x 2)) qs) (some (fn (x) (= x 3)) qs))))) true) ;; --- conde all clauses fail → empty stream --- (mk-test "conde-all-fail" (run* q (conde ((== 1 2)) ((== 3 4)))) (list)) ;; --- empty conde: no clauses ⇒ fail --- (mk-test "conde-no-clauses" (run* q (conde)) (list)) (mk-tests-run!)