diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d7c8083..4b42143b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,4 +146,4 @@ jobs: RUSTDOCFLAGS: '-D warnings' with: command: doc - args: --all --no-deps --document-private-items + args: --all --no-deps --document-private-items --features typenum diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index f61a8ab5..83923d17 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -24,7 +24,7 @@ jobs: name: build docs with: command: doc - args: --all --no-deps + args: --all --no-deps --features typenum - uses: peaceiris/actions-gh-pages@v4 name: push docs diff --git a/Cargo.toml b/Cargo.toml index dab64399..b1e3cdb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "frunk" edition = "2021" -version = "0.4.2" +version = "0.5.0" authors = ["Lloyd "] description = "Frunk provides developers with a number of functional programming tools like HList, Coproduct, Generic, LabelledGeneric, Validated, Monoid, Semigroup and friends." license = "MIT" @@ -19,7 +19,7 @@ time = "0.3" [dependencies.frunk_core] path = "core" default-features = false -version = "0.4.2" +version = "0.5.0" [dependencies.frunk_proc_macros] path = "proc-macros" @@ -45,6 +45,7 @@ default = ["validated", "proc-macros"] validated = ["std"] proc-macros = ["frunk_proc_macros"] std = ["frunk_core/std"] +typenum = ["frunk_core/typenum"] [profile.bench] opt-level = 3 diff --git a/core/Cargo.toml b/core/Cargo.toml index afc58b1b..bb8dbc97 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "frunk_core" edition = "2021" -version = "0.4.2" +version = "0.5.0" authors = ["Lloyd "] description = "Frunk core provides developers with HList, Coproduct, LabelledGeneric and Generic" license = "MIT" @@ -15,9 +15,11 @@ travis-ci = { repository = "lloydmeta/frunk" } [features] default = ["std"] std = [] +typenum = ["dep:typenum"] [dependencies] serde = { version = "^1.0", optional = true, features = [ "derive" ] } +typenum = { version = "1.17.0", optional = true } [dev-dependencies.frunk_derives] path = "../derives" @@ -27,7 +29,7 @@ version = "0.4.1" [dev-dependencies.frunk] path = ".." default-features = false -version = "0.4.2" +version = "0.5.0" [dev-dependencies.frunk_proc_macros] path = "../proc-macros" diff --git a/core/src/hlist.rs b/core/src/hlist.rs index 2fdee009..5284ae29 100644 --- a/core/src/hlist.rs +++ b/core/src/hlist.rs @@ -60,6 +60,11 @@ use crate::traits::{Func, IntoReverse, Poly, ToMut, ToRef}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "typenum")] +pub use typenum; +#[cfg(feature = "typenum")] +use typenum::{bit::B1, Add1, Unsigned, U0}; + use std::ops::Add; /// Typeclass for HList-y behaviour @@ -67,18 +72,58 @@ use std::ops::Add; /// An HList is a heterogeneous list, one that is statically typed at compile time. In simple terms, /// it is just an arbitrarily-nested Tuple2. pub trait HList: Sized { - /// Returns the length of a given HList type without making use of any references, or - /// in fact, any values at all. + /// The type-level encapsulation of the lists length, using `typenum` under the hood. /// /// # Examples /// ``` /// # fn main() { + /// use frunk_core::hlist::typenum::{self, Unsigned }; /// use frunk::prelude::*; /// use frunk_core::HList; /// - /// assert_eq!(::LEN, 3); + /// type LenThree = HList![bool, (), u8]; + /// type LenTwo = HList![bool, ()]; + /// + /// // Attach a constraint that ensures constraints are met at type-check time + /// fn type_len_constraint>() {} + /// + /// + /// + /// // Won't compile: the length of LenThree doesn't meet the less-than-3 requirement + /// // let _fail = type_len_constraint::<::Len>(); + /// let _is_good = type_len_constraint::<::Len>(); + /// + /// // Pull out the length of the list in the word-size of your choosing. + /// let byte: u8 = <::Len>::U8; + /// let u_16: u16 = <::Len>::U16; + /// + /// + /// assert_eq!(::Len::U8, 3u8); + /// # } + /// ``` + /// + /// ```compile_fail + /// # fn main() { + /// use frunk_core::hlist::typenum::{self, Unsigned }; + /// use frunk::prelude::*; + /// use frunk_core::HList; + /// + /// type LenThree = HList![bool, (), u8]; + /// type LenTwo = HList![bool, ()]; + /// + /// // Attach a constraint that ensures constraints are met at type-check time + /// fn type_len_constraint>() {} + /// + /// + /// + /// // Won't compile: the length of LenThree doesn't meet the less-than-3 requirement + /// let _ = type_len_constraint::<::Len>(); /// # } /// ``` + #[cfg(feature = "typenum")] + type Len: Unsigned; + + /// Length as a usize const generic. Is equivilent to `::LEN::USIZE` const LEN: usize; /// Returns the length of a given HList @@ -115,21 +160,6 @@ pub trait HList: Sized { Self::LEN == 0 } - /// Returns the length of a given HList type without making use of any references, or - /// in fact, any values at all. - /// - /// # Examples - /// ``` - /// # fn main() { - /// use frunk::prelude::*; - /// use frunk_core::HList; - /// - /// assert_eq!(::static_len(), 3); - /// # } - /// ``` - #[deprecated(since = "0.1.31", note = "Please use LEN instead")] - fn static_len() -> usize; - /// Prepends an item to the current HList /// /// # Examples @@ -168,10 +198,10 @@ pub trait HList: Sized { pub struct HNil; impl HList for HNil { + #[cfg(feature = "typenum")] + type Len = U0; + const LEN: usize = 0; - fn static_len() -> usize { - Self::LEN - } } /// Represents the most basic non-empty HList. Its value is held in `head` @@ -183,11 +213,18 @@ pub struct HCons { pub tail: T, } +#[cfg(feature = "typenum")] +impl HList for HCons +where + ::Len: Add, + <::Len as Add>::Output: Unsigned, +{ + type Len = <::Len as Add>::Output; + const LEN: usize = 1 + ::LEN; +} +#[cfg(not(feature = "typenum"))] impl HList for HCons { const LEN: usize = 1 + ::LEN; - fn static_len() -> usize { - Self::LEN - } } impl HCons { @@ -1120,9 +1157,25 @@ impl HZippable for HNil { } } +#[cfg(not(feature = "typenum"))] +impl HZippable> for HCons +where + T1: HZippable, +{ + type Zipped = HCons<(H1, H2), T1::Zipped>; + fn zip(self, other: HCons) -> Self::Zipped { + HCons { + head: (self.head, other.head), + tail: self.tail.zip(other.tail), + } + } +} +#[cfg(feature = "typenum")] impl HZippable> for HCons where T1: HZippable, + <>::Zipped as HList>::Len: Add, + Add1<<>::Zipped as HList>::Len>: Unsigned, { type Zipped = HCons<(H1, H2), T1::Zipped>; fn zip(self, other: HCons) -> Self::Zipped { @@ -1433,6 +1486,26 @@ where } } +#[cfg(feature = "typenum")] +#[cfg(feature = "std")] +#[allow(clippy::from_over_into)] +impl Into> for HCons +where + Tail: Into> + HList, + ::Len: Add, + Add1<::Len>: Unsigned, +{ + fn into(self) -> Vec { + let h = self.head; + let t = self.tail; + let mut v = Vec::with_capacity(::LEN); + v.push(h); + let mut t_vec: Vec = t.into(); + v.append(&mut t_vec); + v + } +} +#[cfg(not(feature = "typenum"))] #[cfg(feature = "std")] #[allow(clippy::from_over_into)] impl Into> for HCons @@ -1905,6 +1978,8 @@ mod tests { #[test] fn test_len_const() { + #[cfg(feature = "typenum")] + assert_eq!(::Len::USIZE, 3); assert_eq!(::LEN, 3); } diff --git a/laws/Cargo.toml b/laws/Cargo.toml index 576948b6..42280264 100644 --- a/laws/Cargo.toml +++ b/laws/Cargo.toml @@ -15,7 +15,7 @@ travis-ci = { repository = "lloydmeta/frunk" } [dependencies.frunk] path = ".." default-features = false -version = "0.4.2" +version = "0.5.0" [dependencies] quickcheck = "1.0.3" diff --git a/proc-macro-helpers/Cargo.toml b/proc-macro-helpers/Cargo.toml index a58542f5..67fd7558 100644 --- a/proc-macro-helpers/Cargo.toml +++ b/proc-macro-helpers/Cargo.toml @@ -20,4 +20,4 @@ proc-macro2 = "1" [dependencies.frunk_core] path = "../core" default-features = false -version = "0.4.2" +version = "0.5.0" diff --git a/proc-macros/Cargo.toml b/proc-macros/Cargo.toml index b190ef27..1c2b3070 100644 --- a/proc-macros/Cargo.toml +++ b/proc-macros/Cargo.toml @@ -23,7 +23,7 @@ proc-macro = true [dependencies.frunk_core] path = "../core" default-features = false -version = "0.4.2" +version = "0.5.0" [dependencies.frunk_proc_macro_helpers] path = "../proc-macro-helpers"