Commit Graph

30 Commits

Author SHA1 Message Date
f8b9bde1a5 mk: zip-with-o — element-wise combine of two lists
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Like Haskell's zipWith but relational. (zip-with-o rel l1 l2 result)
applies a 3-arg combiner relation pointwise: rel l1[i] l2[i] result[i].

  (zip-with-o pluso-i (list 1 2 3) (list 10 20 30) q)
    -> ((11 22 33))

  (zip-with-o (fn (a b r) (== r (list a b))) (list :x :y) (list 1 2) q)
    -> (((:x 1) (:y 2)))

Different-length lists fail.

5 new tests, 551/551 cumulative.
2026-05-08 12:31:53 +00:00
2a36e692f4 mk: take-while-o + drop-while-o — predicate-driven prefix/suffix
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
(take-while-o pred l result): take elements from l while pred holds,
stopping at the first element that fails. (drop-while-o pred l result):
drop matching elements, return the rest including the first non-match.

Together: (take-while p l) ⊕ (drop-while p l) = l, verified by an
end-to-end roundtrip test.

8 new tests, 546/546 cumulative.
2026-05-08 12:30:53 +00:00
f4a902a6df mk: nub-o — dedupe by keeping the last occurrence
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 56s
Walks the list; if the head appears in the tail (membero), drop it and
recurse; otherwise keep it and recurse. Result preserves only the
*last* occurrence of each value.

Caveat: with input like (1 1 1) the membero check succeeds with
multiplicity, so multiple (1) answers may emerge — each is shape-
identical, but the test deliberately checks every-result-is-(1) rather
than asserting answer count.

5 new tests, 526/526 cumulative.
2026-05-08 12:27:03 +00:00
091030f13e mk: flat-mapo — concatMap-style relation
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
(flat-mapo rel l result): each element x of l is mapped to a list via
rel x list-from-x, and all such lists are concatenated to form result.

  (flat-mapo (fn (x r) (== r (list x x))) (list 1 2 3) q)
    -> ((1 1 2 2 3 3))

5 new tests, 515/515 cumulative.
2026-05-08 12:24:23 +00:00
f5ab66e1a3 mk: foldl-o — relational left fold
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 33s
Complement to foldr-o. The combiner relation has signature
(acc head new-acc) — accumulator first.

Examples:
  (foldl-o pluso-i (list 1 2 3 4 5) 0 q)  -> (15)
  (foldl-o *o-i    (list 1 2 3 4)   1 q)  -> (24)
  (foldl-o (fn (acc x r) (conso x acc r))  ; flipped conso
           (list 1 2 3 4) (list) q)        -> ((4 3 2 1))   ; reverse

5 new tests, 510/510 cumulative.
2026-05-08 12:23:40 +00:00
3842496f3b mk: foldr-o — relational right fold
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Takes a 3-arg combiner relation, a list, and an initial accumulator,
produces the right-fold result. (rel a tail-result result) combines
the head with the recursive result.

Examples:
  (foldr-o appendo (list (list 1 2) (list 3) (list 4 5)) (list) q)
    -> ((1 2 3 4 5))                ; flatten

  (foldr-o conso (list 1 2 3) (list) q)
    -> ((1 2 3))                    ; rebuild list

4 new tests, 505/505 cumulative.
2026-05-08 12:21:42 +00:00
221c7fef35 mk: partitiono — split list by predicate
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s
(partitiono pred l yes no) — yes is the elements of l where pred
succeeds; no is the rest. Conde dispatches on each element via the
predicate goal vs nafc-of-the-predicate, threading the head through
the matching output list.

Composes with intarith / membero / etc. for any predicate-shaped goal:
  (partitiono (fn (x) (lto-i x 5)) (list 1 7 2 8 3) yes no)
    yes -> (1 2 3); no -> (7 8)

5 new tests, 496/496 cumulative.
2026-05-08 12:18:25 +00:00
363ebc8f04 mk: appendo3 — 3-list append
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Composes two appendos: (appendo a b mid) ∧ (appendo mid c r). Runs
forward (concatenate three known lists) and backward (recover any of
the three from the other two and the result).

5 new tests, 491/491 cumulative.
2026-05-08 12:16:40 +00:00
6454603568 mk: subseto — every element of l1 is in l2
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s
Recursive: empty l1 trivially holds; otherwise the head is in l2 (via
membero) and the tail is a subset. Duplicates in l1 are allowed since
each is independently checked.

7 new tests, 456/456 cumulative.
2026-05-08 12:09:06 +00:00
58d78de32a mk: removeo-allo — remove every occurrence
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 49s
Three conde clauses: empty list -> empty result; head matches x ->
skip and recurse; head differs (nafc-gated) -> keep and recurse.
Distinct from rembero, which removes only the first occurrence.

5 new tests, 446/446 cumulative.
2026-05-08 12:04:17 +00:00
eb69039935 mk: swap-firsto — swap first two list elements
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 48s
Four conso calls express the (a b . rest) -> (b a . rest) rewrite as a
purely relational constraint. Self-inverse on length-2+ lists; runs
forward (swap given input) and backward (recover original from the
swapped form). Fails on lists shorter than 2.

6 new tests, 437/437 cumulative.
2026-05-08 11:59:41 +00:00
c04ddd105b mk: pairlisto — relational zip
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 39s
(pairlisto l1 l2 pairs): pairs is the zipped list of pairs (l1[i] l2[i]).
Recurses on both l1 and l2 in lockstep, building pairs in parallel.

Runs forward, can recover l1 given l2 and pairs, can recover l2 given
l1 and pairs. Different-length lists fail.

5 new tests, 431/431 cumulative.
2026-05-08 11:57:12 +00:00
136cacbd3f mk: iterate-no — apply a relation n times
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
(iterate-no rel n x result) holds when applying the 2-arg relation rel
n times (Peano n) starting from x produces result. Base case: zero
iterations means result equals x. Recursive case: rel x mid, then
iterate-no n-1 from mid.

Generalises common chains:
  succ iteration:  (iterate-no succ-rel n :z q) -> n in Peano
  list growth:     (iterate-no cons-rel n () q) -> n-element list

4 new tests, 426/426 cumulative.
2026-05-08 11:54:24 +00:00
6fc155ddd8 mk: rev-acco + rev-2o — accumulator-style reverse
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 29s
rev-acco is the standard tail-recursive reverse with an accumulator;
rev-2o starts the accumulator at the empty list. Faster than the
appendo-driven reverseo for forward queries because there is no nested
appendo per element.

Trade-off: rev-acco is asymmetric. The accumulator's initial-empty
cannot be enumerated backwards the way reverseo does, so reverseo is
still the right choice when both directions matter.

A test verifies rev-2o and reverseo agree on forward queries.

6 new tests, 422/422 cumulative.
2026-05-08 11:51:51 +00:00
4d861575df mk: selecto — choose element + rest of list
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 29s
Classic miniKanren relation. (selecto x rest l) holds when l contains
x at any position with `rest` being everything else. Direct base case
(l = (x . rest)) plus the skip-head recursion that threads the head
through to the result rest.

Run modes: enumerate every (x, rest) split; recover rest given an
element; recover an element given the rest; (and ground/all combinations).

6 new tests, 411/411 cumulative.
2026-05-08 11:47:27 +00:00
e202c81a0d mk: subo — contiguous sublist relation
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 32s
Composes two appendos: l = front ++ s ++ back, equivalently
  (appendo front-and-s back l) and (appendo front s front-and-s).

Goal order matters: doing the (appendo ground:l) split first makes the
search finitary; the second appendo is then deterministic given
front-and-s and ground s. Reversing the order causes divergence on
failing inputs (the front search becomes unbounded).

7 new tests, 405/405 cumulative.
2026-05-08 11:45:31 +00:00
fc14a8063b mk: prefixo + suffixo — appendo-derived sublist relations
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 40s
Two-line definitions over appendo:
  (prefixo p l) ≡ ∃rest. (appendo p rest l)
  (suffixo s l) ≡ ∃front. (appendo front s l)

Both enumerate all prefixes/suffixes when called with a fresh first
arg, and serve as decision relations when called with both grounded.

9 new tests, 398/398 cumulative.
2026-05-08 11:40:28 +00:00
6ee02db2ab mk: palindromeo — list reads same forwards/backwards
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Two-line definition: a list is a palindrome iff it equals its reverse.
Direct composition of reverseo + ==.

7 new tests: empty / singleton / equal pair / unequal pair /
5-element-yes / 5-element-no / strings.

389/389 cumulative.
2026-05-08 11:38:29 +00:00
7b6cb64548 mk: not-membero — relational "x is not in l"
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
Mirrors the structure all-distincto already uses internally: walk the
list, ensure each element is not equal to x via nafc, recurse on tail.
Useful as a constraint-style filter:

  (membero x (list 1 2 3 4 5))
  (not-membero x (list 2 4))
    -> x in {1, 3, 5}

4 new tests, 382/382 cumulative.
2026-05-08 11:36:14 +00:00
c2b238635f mk: repeato + concato
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
repeato: produces (or recognizes) a list of n copies of a value, with n
Peano-encoded. Runs forward, backward (recover the count from a uniform
list), and bidirectionally.

concato: fold-appendo over a list-of-lists. (concato (list (list 1 2)
(list) (list 3 4 5)) q) -> ((1 2 3 4 5)).

10 new tests, 378/378 cumulative.
2026-05-08 11:34:28 +00:00
8c48a0be63 mk: tako + dropo — Peano-indexed prefix and suffix
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
(tako n l prefix) — prefix is the first n elements of l.
(dropo n l suffix) — suffix is l after dropping the first n.

Both use a Peano natural for the count. Round-trip holds:
  (tako n l) ⊕ (dropo n l) = l   (verified by an end-to-end test)

9 new tests, 368/368 cumulative.
2026-05-08 11:32:33 +00:00
99066430fd mk: lasto + init-o — last and not-last list relations
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 52s
lasto: x is the final element of l. Direct base case (l = (x)) plus
recurse-on-cdr.

init-o: init is l without its last element. Base case for singleton:
(== init ()). Otherwise recurse, threading the head through to the
init result via conso.

Together with appendo, the round-trip init append (list last) = l
holds, which is exercised by an end-to-end test.

8 new tests, 344/344 cumulative.
2026-05-08 11:25:12 +00:00
e7dca2675c mk: everyo / someo — predicate-style relations
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
(everyo rel l): every element of l satisfies the unary relation rel.
(someo rel l): some element does. Both compose with intarith and other
predicate-shaped goals:
  (everyo (fn (x) (lto-i x 10)) (list 1 5 9))    -> succeeds
  (someo  (fn (x) (lto-i 100 x)) (list 5 50 200)) -> succeeds

10 new tests, 310/310 cumulative.
2026-05-08 11:13:22 +00:00
f00054309d mk: mapo (relational map) — 300/300 milestone
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
(mapo rel l1 l2) takes a 2-argument relation rel and asserts l2 is l1
with each element rel-related to its counterpart. Recursive on both
lists in lockstep. Works forward (fixed l1, find l2), backward (fixed
l2, find l1), or constraining mid-pipeline.

Composes with intarith for arithmetic transforms:
  (mapo (fn (a b) (*o-i a a b)) (list 1 2 3 4) q) -> ((1 4 9 16))

7 new tests, 300/300 cumulative.
2026-05-08 11:11:26 +00:00
cfb43a3cdf mk: samelengtho — equal-length relation
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Recurses positionally, dropping a head from each list each step. Both
arguments can be unbound, giving the natural enumeration:

  (run 3 q (fresh (l1 l2) (samelengtho l1 l2) (== q (list l1 l2))))
    -> (((), ())                         empty/empty
        ((_.0), (_.1))                   pair of 1-element lists
        ((_.0 _.1), (_.2 _.3)))          pair of 2-element lists

5 new tests, 293/293 cumulative.
2026-05-08 11:09:48 +00:00
428ca79f61 mk: rembero / assoco / nth-o — more list relations
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
rembero (remove-first) uses nafc to gate the skip-element clause so
the result is well-defined on ground lists. assoco is alist lookup —
runs forward (key -> value) and backward (find keys with a given
value). nth-o uses Peano-encoded indices into a list, mirroring lengtho.

13 new tests, 266/266 cumulative.
2026-05-08 10:50:28 +00:00
bf9fe8b365 mk: flatteno — nested list flattener
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 49s
Three conde clauses: nullo tree -> nullo flat; pairo tree -> recurse on
car & cdr, appendo their flattenings; otherwise tree must be a ground
non-list atom (nafc nullo + nafc pairo) and flat = (list tree).

Works on ground inputs of arbitrary nesting:
  (run* q (flatteno (list 1 (list 2 3) (list (list 4) 5)) q))
    -> ((1 2 3 4 5))

7 tests, 253/253 cumulative. Phase 4 list relations now complete.
2026-05-08 10:46:13 +00:00
b8a0c504bc mk: phase 4C — permuteo (with inserto helper)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 58s
inserto a l p: p is l with a inserted at some position. Recursive: head
of l first, then push past head and recurse.

permuteo l p: classical recursive permutation. Empty -> empty; otherwise
take a head off l, recursively permute the tail, insert head at any
position in the recursive result.

7 new tests including all-6-perms-of-3 as a set check (independent of
generation order). 208/208 cumulative.
2026-05-08 07:22:41 +00:00
f4ab7f2534 mk: phase 4B — reverseo + lengtho, 10 new tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
reverseo: standard recursive definition via appendo. Forward works in
run*; backward (input fresh, output ground) works in run 1 but run*
diverges trying to enumerate the unique answer (canonical TRS issue
with naive reverseo).

lengtho: Peano encoding (:z / (:s :z) / (:s (:s :z)) ...) so it works
relationally in both directions without arithmetic-as-relation. Forward
returns the Peano length; backward enumerates lists of a given length.

162/162 cumulative.
2026-05-07 21:49:38 +00:00
cae87c1e2c mk: phase 4A — appendo canary green, both directions
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Three coupled fixes plus a new relations module land together because
each is required for the next: appendo can't terminate without all
three.

1. unify.sx — added (:cons h t) tagged cons-cell shape because SX has no
   improper pairs. The unifier treats (:cons h t) and the native list
   (h . t) as equivalent. mk-walk* re-flattens cons cells back to flat
   lists for clean reification.

2. stream.sx — switched mature stream cells from plain SX lists to a
   (:s head tail) tagged shape so a mature head can have a thunk tail.
   With the old representation, mk-mplus had to (cons head thunk) which
   SX rejects (cons requires a list cdr).

3. conde.sx — wraps each clause in Zzz (inverse-eta delay) for laziness.
   Zzz uses (gensym "zzz-s-") for the substitution parameter so it does
   not capture user goals that follow the (l s ls) convention. Without
   gensym, every relation that uses `s` as a list parameter silently
   binds it to the substitution dict.

relations.sx is the new module: nullo, pairo, caro, cdro, conso,
firsto, resto, listo, appendo, membero. 25 new tests.

Canary green:
  (run* q (appendo (list 1 2) (list 3 4) q))
    → ((1 2 3 4))
  (run* q (fresh (l s) (appendo l s (list 1 2 3)) (== q (list l s))))
    → ((() (1 2 3)) ((1) (2 3)) ((1 2) (3)) ((1 2 3) ()))
  (run 3 q (listo q))
    → (() (_.0) (_.0 _.1))

152/152 cumulative.
2026-05-07 20:24:42 +00:00