From f00054309d9d27ed5cb5e63f829f1b7884db57f5 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 8 May 2026 11:11:26 +0000 Subject: [PATCH] =?UTF-8?q?mk:=20mapo=20(relational=20map)=20=E2=80=94=203?= =?UTF-8?q?00/300=20milestone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (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. --- lib/minikanren/relations.sx | 8 +++++ lib/minikanren/tests/mapo.sx | 62 ++++++++++++++++++++++++++++++++++++ plans/minikanren-on-sx.md | 4 +++ 3 files changed, 74 insertions(+) create mode 100644 lib/minikanren/tests/mapo.sx diff --git a/lib/minikanren/relations.sx b/lib/minikanren/relations.sx index 200c3128..6c7d6cf0 100644 --- a/lib/minikanren/relations.sx +++ b/lib/minikanren/relations.sx @@ -132,3 +132,11 @@ (conde ((nullo l1) (nullo l2)) ((fresh (a d a-prime d-prime) (conso a d l1) (conso a-prime d-prime l2) (samelengtho d d-prime)))))) + +(define + mapo + (fn + (rel l1 l2) + (conde + ((nullo l1) (nullo l2)) + ((fresh (a d a-prime d-prime) (conso a d l1) (conso a-prime d-prime l2) (rel a a-prime) (mapo rel d d-prime)))))) diff --git a/lib/minikanren/tests/mapo.sx b/lib/minikanren/tests/mapo.sx new file mode 100644 index 00000000..3afbdd04 --- /dev/null +++ b/lib/minikanren/tests/mapo.sx @@ -0,0 +1,62 @@ +;; lib/minikanren/tests/mapo.sx — relational map. + +(mk-test + "mapo-identity" + (run* + q + (mapo (fn (a b) (== a b)) (list 1 2 3) q)) + (list (list 1 2 3))) + +(mk-test + "mapo-tag-each" + (run* + q + (mapo + (fn (a b) (== b (list :tag a))) + (list 1 2 3) + q)) + (list + (list + (list :tag 1) + (list :tag 2) + (list :tag 3)))) + +(mk-test + "mapo-backward" + (run* + q + (mapo (fn (a b) (== a b)) q (list 1 2 3))) + (list (list 1 2 3))) + +(mk-test + "mapo-empty" + (run* q (mapo (fn (a b) (== a b)) (list) q)) + (list (list))) + +(mk-test + "mapo-duplicate" + (run* q (mapo (fn (a b) (== b (list a a))) (list :x :y) q)) + (list (list (list :x :x) (list :y :y)))) + +(mk-test + "mapo-different-length-fails" + (run* + q + (mapo + (fn (a b) (== a b)) + (list 1 2) + (list 1 2 3))) + (list)) + +;; mapo + arithmetic via intarith +(mk-test + "mapo-square-each" + (run* + q + (mapo + (fn (a b) (*o-i a a b)) + (list 1 2 3 4) + q)) + (list (list 1 4 9 16))) + +(mk-tests-run!) diff --git a/plans/minikanren-on-sx.md b/plans/minikanren-on-sx.md index c1d668a2..2fd26a99 100644 --- a/plans/minikanren-on-sx.md +++ b/plans/minikanren-on-sx.md @@ -173,6 +173,10 @@ _(none yet)_ _Newest first._ +- **2026-05-08** — **mapo (relational map) — 300 test milestone**: `(mapo + rel l1 l2)` succeeds when l2 is l1 with each element rel-related. Works + forward and backward; composes with intarith — `(mapo (fn (a b) (*o-i + a a b)) (1 2 3 4) q)` → `((1 4 9 16))`. 7 new tests, **300/300** cumulative. - **2026-05-08** — **samelengtho**: classic miniKanren relation that succeeds when two lists have equal length. Symmetric — works to enumerate both lists fresh: `(run 3 q (fresh (l1 l2) (samelengtho l1 l2) (== q