From 99066430fd07d366e0958b3b9b1ca8e6ab7999e6 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 8 May 2026 11:25:12 +0000 Subject: [PATCH] =?UTF-8?q?mk:=20lasto=20+=20init-o=20=E2=80=94=20last=20a?= =?UTF-8?q?nd=20not-last=20list=20relations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- lib/minikanren/relations.sx | 16 +++++++++++++++ lib/minikanren/tests/lasto.sx | 38 +++++++++++++++++++++++++++++++++++ plans/minikanren-on-sx.md | 4 ++++ 3 files changed, 58 insertions(+) create mode 100644 lib/minikanren/tests/lasto.sx diff --git a/lib/minikanren/relations.sx b/lib/minikanren/relations.sx index 84d4e76b..60200c86 100644 --- a/lib/minikanren/relations.sx +++ b/lib/minikanren/relations.sx @@ -156,3 +156,19 @@ (conde ((fresh (a d) (conso a d l) (rel a))) ((fresh (a d) (conso a d l) (someo rel d)))))) + +(define + lasto + (fn + (l x) + (conde + ((conso x (list) l)) + ((fresh (a d) (conso a d l) (lasto d x)))))) + +(define + init-o + (fn + (l init) + (conde + ((fresh (x) (conso x (list) l) (== init (list)))) + ((fresh (a d d-init) (conso a d l) (conso a d-init init) (init-o d d-init)))))) diff --git a/lib/minikanren/tests/lasto.sx b/lib/minikanren/tests/lasto.sx new file mode 100644 index 00000000..110f8019 --- /dev/null +++ b/lib/minikanren/tests/lasto.sx @@ -0,0 +1,38 @@ +;; lib/minikanren/tests/lasto.sx — last-element + init-without-last. + +(mk-test + "lasto-singleton" + (run* q (lasto (list 5) q)) + (list 5)) +(mk-test + "lasto-multi" + (run* q (lasto (list 1 2 3 4) q)) + (list 4)) +(mk-test "lasto-empty" (run* q (lasto (list) q)) (list)) + +(mk-test "lasto-strings" (run* q (lasto (list "a" "b" "c") q)) (list "c")) + +(mk-test + "init-o-multi" + (run* q (init-o (list 1 2 3 4) q)) + (list (list 1 2 3))) + +(mk-test + "init-o-singleton" + (run* q (init-o (list 7) q)) + (list (list))) + +(mk-test "init-o-empty" (run* q (init-o (list) q)) (list)) + +(mk-test + "lasto-init-o-roundtrip" + (run* + q + (fresh + (init last) + (lasto (list 1 2 3 4) last) + (init-o (list 1 2 3 4) init) + (appendo init (list last) q))) + (list (list 1 2 3 4))) + +(mk-tests-run!) diff --git a/plans/minikanren-on-sx.md b/plans/minikanren-on-sx.md index fd39efa1..3515133f 100644 --- a/plans/minikanren-on-sx.md +++ b/plans/minikanren-on-sx.md @@ -173,6 +173,10 @@ _(none yet)_ _Newest first._ +- **2026-05-08** — **lasto / init-o**: classic head/tail-style list relations. + lasto extracts the final element; init-o extracts everything-but-the-last. + Together with appendo, the round-trip `init ⊕ (last) = l` holds. 8 new + tests, 344/344 cumulative. - **2026-05-08** — **Datalog-style relational database queries**: tests/rdb.sx shows miniKanren as a query engine over fact tables. Defines two tables (employees + project assignments), wraps each with a relation that does