;; lib/minikanren/conde.sx — Phase 2 piece C: `conde`, the canonical ;; miniKanren and-or form, with implicit Zzz inverse-eta delay so recursive ;; relations like appendo terminate. ;; ;; (conde (g1a g1b ...) (g2a g2b ...) ...) ;; ≡ (mk-disj (Zzz (mk-conj g1a g1b ...)) ;; (Zzz (mk-conj g2a g2b ...)) ...) ;; ;; `Zzz g` wraps a goal expression in (fn (S) (fn () (g S))) so that ;; `g`'s body isn't constructed until the surrounding fn is applied to a ;; substitution AND the returned thunk is forced. This is what gives ;; miniKanren its laziness — recursive goal definitions can be `(conde ;; ... (... (recur ...)))` without infinite descent at construction time. ;; ;; Hygiene: the substitution parameter is gensym'd so that user goal ;; expressions which themselves bind `s` (e.g. `(appendo l s ls)`) keep ;; their lexical `s` and don't accidentally reference the wrapper's ;; substitution. Without gensym, miniKanren relations that follow the ;; common (l s ls) parameter convention are silently miscompiled. (defmacro Zzz (g) (let ((s-sym (gensym "zzz-s-"))) (quasiquote (fn ((unquote s-sym)) (fn () ((unquote g) (unquote s-sym))))))) (defmacro conde (&rest clauses) (quasiquote (mk-disj (splice-unquote (map (fn (clause) (quasiquote (Zzz (mk-conj (splice-unquote clause))))) clauses)))))