Skip to content
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

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion .github/workflows/rustdoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
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"
Copy link
Author

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...

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"
Expand All @@ -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"
Expand Down
6 changes: 4 additions & 2 deletions core/Cargo.toml
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"
Expand All @@ -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"
Expand All @@ -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"
Expand Down
123 changes: 99 additions & 24 deletions core/src/hlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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`
Expand All @@ -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
Copy link
Author

Choose a reason for hiding this comment

The 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> {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1433,6 +1486,26 @@ where
}
}

#[cfg(feature = "typenum")]
Copy link
Author

Choose a reason for hiding this comment

The 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 let ??? = list.into(); let vector = ???.into();

#[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>
Expand Down Expand Up @@ -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
Copy link
Author

Choose a reason for hiding this comment

The 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);
}

Expand Down
2 changes: 1 addition & 1 deletion laws/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 1 addition & 1 deletion proc-macro-helpers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ proc-macro2 = "1"
[dependencies.frunk_core]
path = "../core"
default-features = false
version = "0.4.2"
version = "0.5.0"
2 changes: 1 addition & 1 deletion proc-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down