-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typenum list #232
base: master
Are you sure you want to change the base?
Typenum list #232
Changes from 24 commits
10960f6
5678174
001a667
bdad2d1
a024384
f2ff09c
91f4442
e0783fc
b1521ae
053516a
327efd1
e3995ca
cb668a9
56484d5
8f83135
8d687fe
6a86de8
02bbdf8
8a9775a
f39266f
ce74350
906c499
7cb7052
fb310a6
424d769
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
[package] | ||
name = "frunk" | ||
edition = "2021" | ||
version = "0.4.2" | ||
version = "0.5.0" | ||
authors = ["Lloyd <[email protected]>"] | ||
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" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
[package] | ||
name = "frunk_core" | ||
edition = "2021" | ||
version = "0.4.2" | ||
version = "0.5.0" | ||
authors = ["Lloyd <[email protected]>"] | ||
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" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,25 +60,70 @@ 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 | ||
/// | ||
/// 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!(<HList![i32, bool, f32]>::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<T: typenum::IsLess<typenum::U3, Output = typenum::True>>() {} | ||
/// | ||
/// | ||
/// | ||
/// // Won't compile: the length of LenThree doesn't meet the less-than-3 requirement | ||
/// // let _fail = type_len_constraint::<<LenThree as HList>::Len>(); | ||
/// let _is_good = type_len_constraint::<<LenTwo as HList>::Len>(); | ||
/// | ||
/// // Pull out the length of the list in the word-size of your choosing. | ||
/// let byte: u8 = <<LenThree as HList>::Len>::U8; | ||
/// let u_16: u16 = <<LenThree as HList>::Len>::U16; | ||
/// | ||
/// | ||
/// assert_eq!(<LenThree as HList>::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<T: typenum::IsLess<typenum::U3, Output = typenum::True>>() {} | ||
/// | ||
/// | ||
/// | ||
/// // Won't compile: the length of LenThree doesn't meet the less-than-3 requirement | ||
/// let _ = type_len_constraint::<<LenThree as HList>::Len>(); | ||
/// # } | ||
/// ``` | ||
#[cfg(feature = "typenum")] | ||
type Len: Unsigned; | ||
|
||
/// Length as a usize const generic. Is equivilent to `<Self as HList>::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!(<HList![i32, bool, f32]>::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<H, T> { | |
pub tail: T, | ||
} | ||
|
||
#[cfg(feature = "typenum")] | ||
impl<H, T: HList> HList for HCons<H, T> | ||
where | ||
<T as HList>::Len: Add<B1>, | ||
<<T as HList>::Len as Add<B1>>::Output: Unsigned, | ||
{ | ||
type Len = <<T as HList>::Len as Add<B1>>::Output; | ||
const LEN: usize = 1 + <T as HList>::LEN; | ||
} | ||
#[cfg(not(feature = "typenum"))] | ||
impl<H, T: HList> HList for HCons<H, T> { | ||
const LEN: usize = 1 + <T as HList>::LEN; | ||
Comment on lines
+216
to
227
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any suggestions RE: this duplication? it's exactly the same except for the added where-clases, but if there's a way to feature gate where-clases only, I'm unfamiliar |
||
fn static_len() -> usize { | ||
Self::LEN | ||
} | ||
} | ||
|
||
impl<H, T> HCons<H, T> { | ||
|
@@ -1120,9 +1157,25 @@ impl HZippable<HNil> for HNil { | |
} | ||
} | ||
|
||
#[cfg(not(feature = "typenum"))] | ||
impl<H1, T1, H2, T2> HZippable<HCons<H2, T2>> for HCons<H1, T1> | ||
where | ||
T1: HZippable<T2>, | ||
{ | ||
type Zipped = HCons<(H1, H2), T1::Zipped>; | ||
fn zip(self, other: HCons<H2, T2>) -> Self::Zipped { | ||
HCons { | ||
head: (self.head, other.head), | ||
tail: self.tail.zip(other.tail), | ||
} | ||
} | ||
} | ||
#[cfg(feature = "typenum")] | ||
impl<H1, T1, H2, T2> HZippable<HCons<H2, T2>> for HCons<H1, T1> | ||
where | ||
T1: HZippable<T2>, | ||
<<T1 as HZippable<T2>>::Zipped as HList>::Len: Add<B1>, | ||
Add1<<<T1 as HZippable<T2>>::Zipped as HList>::Len>: Unsigned, | ||
{ | ||
type Zipped = HCons<(H1, H2), T1::Zipped>; | ||
fn zip(self, other: HCons<H2, T2>) -> Self::Zipped { | ||
|
@@ -1433,6 +1486,26 @@ where | |
} | ||
} | ||
|
||
#[cfg(feature = "typenum")] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A bit out of scope for this PR, but I feel this should return an array of known-length, and it be up to the library user to say "no, I want it as a Vec". Perhaps a middle ground though: implement the into array, and into vec is provided as a conveniece wrapper arround |
||
#[cfg(feature = "std")] | ||
#[allow(clippy::from_over_into)] | ||
impl<H, Tail> Into<Vec<H>> for HCons<H, Tail> | ||
where | ||
Tail: Into<Vec<H>> + HList, | ||
<Tail as HList>::Len: Add<B1>, | ||
Add1<<Tail as HList>::Len>: Unsigned, | ||
{ | ||
fn into(self) -> Vec<H> { | ||
let h = self.head; | ||
let t = self.tail; | ||
let mut v = Vec::with_capacity(<Self as HList>::LEN); | ||
v.push(h); | ||
let mut t_vec: Vec<H> = t.into(); | ||
v.append(&mut t_vec); | ||
v | ||
} | ||
} | ||
#[cfg(not(feature = "typenum"))] | ||
#[cfg(feature = "std")] | ||
#[allow(clippy::from_over_into)] | ||
impl<H, Tail> Into<Vec<H>> for HCons<H, Tail> | ||
|
@@ -1905,6 +1978,8 @@ mod tests { | |
|
||
#[test] | ||
fn test_len_const() { | ||
#[cfg(feature = "typenum")] | ||
assert_eq!(<HList![usize, &str, f32] as HList>::Len::USIZE, 3); | ||
Comment on lines
+1981
to
+1982
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More testing needed? |
||
assert_eq!(<HList![usize, &str, f32] as HList>::LEN, 3); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this version bumb still need to happen? except for the removal of a long-deprecated function, all changes are backwards compatable and additions are feature-gated. perhaps a bump to 0.4.3 instead?
...then again, pre-major reseales, bumps are pretty much free anyway...