[no description]
bendn 13 days ago
commit 4e8923e
-rw-r--r--.gitignore1
-rw-r--r--Cargo.toml10
-rw-r--r--LICENSE21
-rw-r--r--README.md3
-rw-r--r--build.rs97
-rw-r--r--src/implementations.rs150
-rw-r--r--src/lib.rs170
-rw-r--r--src/picks.rs33
8 files changed, 485 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..03314f7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..503a575
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "ttools"
+version = "0.1.0"
+license = "MIT"
+edition = "2024"
+authors = ["bend-n <[email protected]>"]
+description = "tuple utilities"
+repository = "https://git.bendn.org/ttools"
+keywords = ["tuple", "utilities"]
+categories = ["algorithms", "no-std"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8c33164
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 bendn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a67b54b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# `ttools`
+
+for when you really, really, wanna pretend your tuple is a heterogenous array.
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..1fabe52
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,97 @@
+use std::{fs::File, io::Write, path::Path};
+
+fn main() {
+ let e = std::env::var("OUT_DIR").unwrap();
+ let mut impl_td = File::create(Path::new(&e).join("impl_td.rs")).unwrap();
+ let mut impl_rv = File::create(Path::new(&e).join("impl_rv.rs")).unwrap();
+ let mut impl_pk = File::create(Path::new(&e).join("impl_pk.rs")).unwrap();
+ for n in 0..26 {
+ let z = ('A'..='Z').take(n).collect::<Vec<_>>().into_iter();
+ generate(z, &mut impl_td, &mut impl_rv, &mut impl_pk);
+ }
+}
+fn tup(x: impl Iterator<Item = char>) -> String {
+ let mut s = String::default();
+ for lem in x {
+ s.push(lem);
+ s.push(',');
+ }
+
+ s
+}
+fn generate(
+ x: impl Iterator<Item = char> + Clone + DoubleEndedIterator + ExactSizeIterator,
+ impl_td: &mut impl Write,
+ impl_rv: &mut impl Write,
+ impl_pk: &mut impl Write,
+) {
+ let tupl = tup(x.clone());
+ let n = x.clone().count();
+ for i in 0..n.min(10) {
+ let drop = tup(x.clone().skip(i));
+ let take = tup(x.clone().take(i));
+ let take_b = tup(x.clone().rev().take(i).rev());
+ let drop_b = tup(x.clone().rev().skip(i).rev());
+ write!(
+ impl_td,
+ "
+ impl<{tupl}> TD<{i}> for ({tupl}) {{
+ type Drop = ({drop});
+ type Take = ({take});
+ type TakeB = ({take_b});
+ type DropB = ({drop_b});
+
+ fn dropf(self) -> (Self::Take, Self::Drop) {{
+ let ({tupl}) = self;
+ (({take}), ({drop}))
+ }}
+
+ fn dropb(self) -> (Self::DropB, Self::TakeB) {{
+ let ({tupl}) = self;
+ (({drop_b}), ({take_b}))
+ }}
+ }}
+ ",
+ )
+ .unwrap();
+
+ write!(
+ impl_pk,
+ "
+ impl<{tupl}> Pick<{i}> for ({tupl}) {{
+ type At = {at};
+ type L = ({left});
+ type R = ({right});
+ fn repick(({left}): Self::L, at: Self::At, ({right}): Self::R) -> Self {{
+ ({left} at, {right})
+ }}
+ fn depict(self) -> (Self::L, Self::At, Self::R) {{
+ let ({tupl}) = self;
+ (({left}), {at}, ({right}))
+ }}
+ }}
+ ",
+ at = x.clone().nth(i).unwrap(),
+ left = tup(x.clone().take(i)),
+ right = tup(x.clone().skip(i + 1))
+ )
+ .unwrap();
+ }
+ let rev = tup(x.clone().rev());
+ write!(
+ impl_rv,
+ "
+ #[allow(non_snake_case)]
+ impl<{tupl}> Reverse for ({tupl}) {{
+ type Reversed = ({rev});
+
+ fn reverse(self) -> Self::Reversed {{
+ let ({x}) = self;
+ ({rev})
+ }}
+ }}
+ ",
+ x = tup(x.clone()),
+ )
+ .unwrap();
+}
diff --git a/src/implementations.rs b/src/implementations.rs
new file mode 100644
index 0000000..2f2eb54
--- /dev/null
+++ b/src/implementations.rs
@@ -0,0 +1,150 @@
+#![allow(non_snake_case)]
+use super::*;
+include!(concat!(env!("OUT_DIR"), "/impl_td.rs"));
+include!(concat!(env!("OUT_DIR"), "/impl_rv.rs"));
+include!(concat!(env!("OUT_DIR"), "/impl_pk.rs"));
+
+macro_rules! generate {(
+ $($Hd:tt $($T:tt)*)?
+) => (
+ $(generate! { $($T)* })?
+ do_impl! { [$] $($($T)* [$] $Hd)? }
+)}
+// impl Reverse for () {
+// type Reversed = ();
+
+// fn reverse(self) -> Self::Reversed {
+// ()
+// }
+// }
+impl Tupl for () {
+ const LEN: usize = 0;
+ type Head = !;
+ type Last = !;
+ type Tail = ();
+ type Init = ();
+ type Inner = ();
+
+ fn uncons(self) -> (Self::Head, Self::Tail) {
+ todo!()
+ }
+ fn unsnoc(self) -> (Self::Init, Self::Last) {
+ todo!()
+ }
+ fn fcons(head: Self::Head, _: Self::Inner, _: Self::Last) -> Self {
+ match head {}
+ }
+ fn cons(head: Self::Head, _: Self::Tail) -> Self {
+ match head {}
+ }
+ fn snoc(_: Self::Init, last: Self::Last) -> Self {
+ match last {}
+ }
+}
+impl<T> Tupl for (T,) {
+ const LEN: usize = 1;
+ type Head = T;
+ type Last = T;
+ type Tail = ();
+ type Init = ();
+ type Inner = ();
+
+ fn uncons(self) -> (Self::Head, Self::Tail) {
+ unimplemented!("dont call base case.")
+ }
+
+ fn unsnoc(self) -> (Self::Init, Self::Last) {
+ unimplemented!("dont call base case.")
+ }
+
+ fn cons(_: Self::Head, _: Self::Tail) -> Self {
+ unimplemented!("dont call base case.")
+ }
+ fn snoc(_: Self::Init, _: Self::Last) -> Self {
+ unimplemented!("dont call base case.")
+ }
+ fn fcons(_: Self::Head, _: Self::Inner, _: Self::Last) -> Self {
+ unimplemented!("dont call base case.")
+ }
+}
+
+macro_rules! do_impl {
+([$_:tt]) => {};
+([$_:tt] [$] $i:tt) => {};
+// ([$_:tt] $i:ident [$] $x:tt) => {};
+(
+ [$_:tt] // `$` sigil
+ $hd:tt $($i:ident)* [$] $tl:tt
+) => (
+ // impl<T> Seal for ($($i, )*) {}
+ impl<$($i,)* Α> Cons<Α> for ($($i,)*) {
+ type Cons = (Α, $($i,)*);
+ type Snoc = ($($i,)* Α,);
+ }
+
+ #[allow(non_snake_case)]
+ impl<$hd, $($i,)* $tl> Tupl for ($hd, $($i,)* $tl) {
+ const LEN: usize = 0 $(+ { stringify!($i); 1 } )*;
+ type Head = $hd;
+ type Last = $tl;
+ type Tail = ($($i,)* $tl,);
+ type Init = ($hd, $($i,)*);
+ type Inner = ($($i,)*);
+ fn cons(head: Self::Head, ($($i,)* $tl,): Self::Tail) -> Self {
+ (head, $($i,)* $tl,)
+ }
+ fn snoc(($hd, $($i,)*): Self::Init, last: Self::Last) -> Self {
+ ($hd, $($i,)* last)
+ }
+ fn fcons(head: Self::Head, ($($i,)*): Self::Inner, last: Self::Last) -> Self {
+ (head, $($i,)* last)
+ }
+ fn uncons(self) -> (Self::Head, Self::Tail) {
+ let ($hd, $($i,)* $tl) = self;
+ ($hd, ($($i,)* $tl,))
+ }
+ fn unsnoc(self) -> (Self::Init, Self::Last) {
+ let ($hd, $($i,)* $tl) = self;
+ (($hd, $($i,)*), $tl)
+ }
+ // fn array(self) -> [T; 0 $(+ { stringify!($i); 1 } )*] {
+ // with_vars! {
+ // [acc: ]
+ // [to_munch: $($i)*]
+ // |$_ x| {
+ // let ($_($_ x, )*) = self;
+ // [$_($_ x, )*]
+ // }
+ // }
+ // }
+ // fn tuple(x: [T; 0 $(+ { stringify!($i); 1 } )*]) -> ($($i, )*) {
+ // with_vars! {
+ // [acc: ]
+ // [to_munch: $($i)*]
+ // |$_ x| {
+ // let [$_($x, )*] = x;
+ // ($_($x, )*)
+ // }
+ // }
+ // }
+ }
+);
+}
+
+generate!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);
+
+impl<T, U: Cons<T>> Push<T> for U {
+ type Result = U::Snoc;
+
+ fn push(self, other: T) -> Self::Result {
+ crate::snoc(self, other)
+ }
+}
+
+impl<T, U: Cons<T>> With<U> for T {
+ type Result = U::Cons;
+
+ fn with(self, other: U) -> Self::Result {
+ crate::cons(self, other)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..2b2f999
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,170 @@
+#![feature(tuple_trait, import_trait_associated_functions, never_type)]
+#![no_std]
+#![allow(mixed_script_confusables, confusable_idents)]
+//! ![view](https://raw.githubusercontent.com/learnyouahaskell/learnyouahaskell.github.io/e4282695721ed62b746d213aa81265f8c254fbf2/static/assets/images/starting-out/listmonster.png)
+//!
+//! start with [`Tupl`].
+mod implementations;
+mod picks;
+/// Reverse a tuple.
+pub trait Reverse: Tupl {
+ type Reversed: Tupl;
+ fn reverse(self) -> Self::Reversed;
+}
+#[doc(inline)]
+pub use Reverse::{reverse, reverse as flip};
+#[doc(inline)]
+pub use Tupl::{drop, head, head as left, head as fst, last, last as right, pick, take};
+pub use picks::*;
+/// Construct a tuple.
+pub fn cons<T, Tup: Cons<T>>(x: T, y: Tup) -> Tup::Cons {
+ Tup::Cons::cons(x, y)
+}
+/// Construct a tuple, backwards.
+pub fn snoc<T, Tup: Cons<T>>(x: Tup, y: T) -> Tup::Snoc {
+ Tup::Snoc::snoc(x, y)
+}
+/// alias for [`snoc`]
+pub trait Push<T>: Tupl {
+ type Result;
+ /// ```
+ /// use ttools::*;
+ /// let x = (1, 2).push(3);
+ /// assert_eq!(x, ((1, 2, 3)));
+ /// ```
+ fn push(self, other: T) -> Self::Result;
+}
+/// alias for [`cons`]
+pub trait With<T: Tupl> {
+ type Result;
+ /// ```
+ /// use ttools::*;
+ /// let x = 1.with((2, 3));
+ /// assert_eq!(x, ((1, 2, 3)));
+ /// ```
+ fn with(self, other: T) -> Self::Result;
+}
+
+// implemented for the constructed type
+pub trait Cons<T>: Tupl {
+ /// (T, ..self)
+ type Cons: Tupl<Head = T, Tail = Self>;
+ /// (..self, T)
+ type Snoc: Tupl<Last = T, Init = Self>;
+}
+/// backing trait for [`take`].
+pub trait TD<const N: usize>: Tupl {
+ /// self[N..]
+ type Drop: Tupl;
+ /// self[..N]
+ type Take: Tupl;
+ // self[LEN - N..]
+ type TakeB: Tupl;
+ /// self[..LEN - N]
+ type DropB: Tupl;
+ /// deconstruct
+ fn dropf(self) -> (Self::Take, Self::Drop);
+ /// deconstruct
+ fn dropb(self) -> (Self::DropB, Self::TakeB);
+}
+/// backing trait for [`pick`]
+pub trait Pick<const N: usize>: Tupl {
+ /// `self[N]`
+ type At;
+ /// self[..N]
+ type L: Tupl;
+ /// self[N + 1..]
+ type R: Tupl;
+ /// reconstruct
+ fn repick(l: Self::L, at: Self::At, r: Self::R) -> Self;
+ /// deconstruct
+ fn depict(self) -> (Self::L, Self::At, Self::R);
+}
+/// Main tuple trait.
+pub trait Tupl: core::marker::Tuple {
+ /// Refer to listmonster.
+ type Head;
+ /// Refer to listmonster.
+ type Last;
+ /// Refer to listmonster.
+ type Tail: Tupl;
+ /// Refer to listmonster.
+ type Init: Tupl;
+ /// Middle section of listmonster.
+ type Inner: Tupl;
+ /// Size of tuple.
+ const LEN: usize;
+ /// Note: Partial function.
+ #[doc(alias = "left")]
+ fn head(self) -> Self::Head
+ where
+ Self: Sized,
+ {
+ self.uncons().0
+ }
+ /// Note: Partial function.
+ #[doc(alias = "right")]
+ fn last(self) -> Self::Last
+ where
+ Self: Sized,
+ {
+ self.unsnoc().1
+ }
+
+ /// Note: Partial function.
+ fn take<const N: usize>(self) -> Self::Take
+ where
+ Self: TD<N> + Sized,
+ {
+ self.dropf().0
+ }
+ /// Note: Partial function.
+ fn drop<const N: usize>(self) -> Self::Drop
+ where
+ Self: TD<N> + Sized,
+ {
+ self.dropf().1
+ }
+ /// Note: Partial function.
+ fn take_back<const N: usize>(self) -> Self::TakeB
+ where
+ Self: TD<N> + Sized,
+ {
+ self.dropb().1
+ }
+ /// Note: Partial function.
+ fn drop_back<const N: usize>(self) -> Self::DropB
+ where
+ Self: TD<N> + Sized,
+ {
+ self.dropb().0
+ }
+ /// Gives you the element at index N (from 0).
+ /// Note: partial function
+ fn pick<const N: usize>(self) -> Self::At
+ where
+ Self: Pick<N> + Sized,
+ {
+ self.depict().1
+ }
+
+ /// unconstructs self, returning head and tail (see listmonster)
+ fn uncons(self) -> (Self::Head, Self::Tail);
+ /// untcurtsnocs self, returning init and last (see listmonster)
+ fn unsnoc(self) -> (Self::Init, Self::Last);
+ /// weird thing, full construct
+ fn fcons(start: Self::Head, inner: Self::Inner, last: Self::Last) -> Self;
+ /// reconstruct; dont use directly
+ fn cons(head: Self::Head, tail: Self::Tail) -> Self;
+ /// retcurtsnoc; dont use directly
+ fn snoc(init: Self::Init, last: Self::Last) -> Self;
+}
+
+#[test]
+fn t() {
+ 2.with(cons(4i8, (1i16, 2i32)).push(2))
+ .take::<2>()
+ .push(4)
+ .push(0u8)
+ .pick::<3>();
+}
diff --git a/src/picks.rs b/src/picks.rs
new file mode 100644
index 0000000..2b862f1
--- /dev/null
+++ b/src/picks.rs
@@ -0,0 +1,33 @@
+use super::*;
+/// second
+pub fn snd<T: Tupl + Pick<{ 2 - 1 }>>(x: T) -> T::At {
+ x.pick::<1>()
+}
+/// third
+pub fn thrd<T: Tupl + Pick<{ 3 - 1 }>>(x: T) -> T::At {
+ x.pick::<2>()
+}
+/// fourth
+pub fn frth<T: Tupl + Pick<{ 4 - 1 }>>(x: T) -> T::At {
+ x.pick::<3>()
+}
+/// fifth
+pub fn ffth<T: Tupl + Pick<{ 5 - 1 }>>(x: T) -> T::At {
+ x.pick::<4>()
+}
+/// sixth
+pub fn sxth<T: Tupl + Pick<{ 6 - 1 }>>(x: T) -> T::At {
+ x.pick::<5>()
+}
+/// seventh
+pub fn svnt<T: Tupl + Pick<{ 7 - 1 }>>(x: T) -> T::At {
+ x.pick::<6>()
+}
+/// eigth
+pub fn eith<T: Tupl + Pick<{ 8 - 1 }>>(x: T) -> T::At {
+ x.pick::<7>()
+}
+/// ninth
+pub fn nint<T: Tupl + Pick<{ 9 - 1 }>>(x: T) -> T::At {
+ x.pick::<8>()
+}