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 4 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
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
5 changes: 3 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 @@ -18,6 +18,7 @@ std = []

[dependencies]
serde = { version = "^1.0", optional = true, features = [ "derive" ] }
typenum = { version = "1.17.0", default-features = false }

[dev-dependencies.frunk_derives]
path = "../derives"
Expand All @@ -27,7 +28,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
51 changes: 37 additions & 14 deletions core/src/hlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ use crate::indices::{Here, Suffixed, There};
use crate::traits::{Func, IntoReverse, Poly, ToMut, ToRef};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use typenum::bit::B1;
use typenum::{Unsigned, UInt, UTerm};

use std::ops::Add;

Expand All @@ -67,19 +69,26 @@ 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 typenum::Unsigned;
/// use frunk::prelude::*;
/// use frunk_core::HList;
///
/// assert_eq!(<HList![i32, bool, f32]>::LEN, 3);
/// type LenThree = HList![bool, (), u8];
/// fn foo<T: typenum::IsEqual<typenum::U3>>() {}
///
/// let _ = foo::<<LenThree as HList>::Len>();
/// let byte: u8 = <<LenThree as HList>::Len>::U8;
///
///
/// assert_eq!(<LenThree as HList>::Len::USIZE, 3);
/// # }
/// ```
const LEN: usize;
type Len: Unsigned;

/// Returns the length of a given HList
///
Expand All @@ -93,9 +102,10 @@ pub trait HList: Sized {
/// assert_eq!(h.len(), 2);
/// # }
/// ```
#[deprecated(since = "0.5.0", note = "Please use <Self as HList>::Len::[USIZE | U8 | U32 | ... ] instead")]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would not make much sense to deprecate this method and push users towards using the Typenum-coupled type.

#[inline]
fn len(&self) -> usize {
Self::LEN
<Self::Len as Unsigned>::USIZE
}

/// Returns whether a given HList is empty
Expand All @@ -112,7 +122,7 @@ pub trait HList: Sized {
/// ```
#[inline]
fn is_empty(&self) -> bool {
Self::LEN == 0
<Self::Len as Unsigned>::USIZE == 0
}

/// Returns the length of a given HList type without making use of any references, or
Expand All @@ -127,7 +137,7 @@ pub trait HList: Sized {
/// assert_eq!(<HList![i32, bool, f32]>::static_len(), 3);
/// # }
/// ```
#[deprecated(since = "0.1.31", note = "Please use LEN instead")]
#[deprecated(since = "0.1.31", note = "Please use Len::USIZE instead")]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, I think we should probably think about finally removing this long-deprecated method..

fn static_len() -> usize;

/// Prepends an item to the current HList
Expand Down Expand Up @@ -168,9 +178,9 @@ pub trait HList: Sized {
pub struct HNil;

impl HList for HNil {
const LEN: usize = 0;
type Len = typenum::U0;
fn static_len() -> usize {
Self::LEN
<Self::Len as Unsigned>::USIZE
}
}

Expand All @@ -183,10 +193,14 @@ pub struct HCons<H, T> {
pub tail: T,
}

impl<H, T: HList> HList for HCons<H, T> {
const LEN: usize = 1 + <T as HList>::LEN;
impl<H, T: HList> HList for HCons<H, T>
where
<T as HList>::Len: Add<typenum::U1>,
<<T as HList>::Len as Add<typenum::U1>>::Output: Unsigned
{
type Len = <<T as HList>::Len as Add<typenum::U1>>::Output;
fn static_len() -> usize {
Self::LEN
<Self::Len as Unsigned>::USIZE
}
}

Expand Down Expand Up @@ -252,6 +266,9 @@ macro_rules! gen_inherent_methods {
pub fn len(&self) -> usize
where Self: HList,
{
// this is how it's done at the type-level
// <Self as HList>::Len::USIZE
#[allow(deprecated)]
HList::len(self)
}

Expand All @@ -272,6 +289,8 @@ macro_rules! gen_inherent_methods {
where Self: HList,
{
HList::is_empty(self)
// this is how it's done at the type-level
// <<<Self as HList>::Len as typenum::IsEqual<typenum::U0>>::Output as typenum::Bit>::BOOL
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left this in to illustrate that this change doesn't forec the new way of doing things. Things can get pretty gnarly.

}

/// Prepend an item to the current HList
Expand Down Expand Up @@ -1123,6 +1142,8 @@ impl HZippable<HNil> for HNil {
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<UInt<UTerm, B1>>,
<<<T1 as HZippable<T2>>::Zipped as HList>::Len as Add<UInt<UTerm, B1>>>::Output: Unsigned,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a) we need to pull out the type-defined length of the post-zipped list, and ensure that it is a type that implements the Add trait, effectively "the type cat play the role of a in a + 1"
b) with that addition constaint satisfied, we need to get the output, and ensure that it is a type that can fill the roll of b in a + 1 = b

{
type Zipped = HCons<(H1, H2), T1::Zipped>;
fn zip(self, other: HCons<H2, T2>) -> Self::Zipped {
Expand Down Expand Up @@ -1438,11 +1459,13 @@ where
impl<H, Tail> Into<Vec<H>> for HCons<H, Tail>
where
Tail: Into<Vec<H>> + HList,
<Tail as HList>::Len: Add<UInt<UTerm, B1>>,
<<Tail as HList>::Len as Add<UInt<UTerm, B1>>>::Output: Unsigned,
{
fn into(self) -> Vec<H> {
let h = self.head;
let t = self.tail;
let mut v = Vec::with_capacity(<Self as HList>::LEN);
let mut v = Vec::with_capacity(<<Self as HList>::Len as Unsigned>::USIZE);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One option is to return an array instead of a vector, because now the length is fundementally know at compile time. GenericArray could be used, making this a no_std compatable conversion

v.push(h);
let mut t_vec: Vec<H> = t.into();
v.append(&mut t_vec);
Expand Down Expand Up @@ -1905,7 +1928,7 @@ mod tests {

#[test]
fn test_len_const() {
assert_eq!(<HList![usize, &str, f32] as HList>::LEN, 3);
assert_eq!(<HList![usize, &str, f32] as HList>::Len::USIZE, 3);
}

#[test]
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
Loading