-
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
Conversation
core/src/hlist.rs
Outdated
// this is how it's done at the type-level | ||
// <<<Self as HList>::Len as typenum::IsEqual<typenum::U0>>::Output as typenum::Bit>::BOOL |
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.
I've left this in to illustrate that this change doesn't forec the new way of doing things. Things can get pretty gnarly.
core/src/hlist.rs
Outdated
<<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, |
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.
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
core/src/hlist.rs
Outdated
let mut v = Vec::with_capacity(<Self as HList>::LEN); | ||
let mut v = Vec::with_capacity(<<Self as HList>::Len as Unsigned>::USIZE); |
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.
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
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.
Overall, I understand the motivation for this. At the same time, I'm weighing if this should be in frunk , and thus force a dependency on typenum
and associated compile-time-hit, or whether users who need this should just enable nightly
and use the generic_const_exprs
option (playground example).
core/src/hlist.rs
Outdated
@@ -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")] |
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.
I think it would not make much sense to deprecate this method and push users towards using the Typenum-coupled type.
core/src/hlist.rs
Outdated
@@ -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")] |
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.
On the other hand, I think we should probably think about finally removing this long-deprecated method..
The problem arises when you want to apply constraints such as length. This fix addresses a limitation of compile-time constant generics, but doesn't fully leverage the type system. For example, what if I want to constrain to a particular length? or that any given trait implementation, two values assosciated with a trait must satisfy some sort of truth? (a == b/2, or a < b). I'm not aware of any features, nightly or otherwise, that says you can do something like this: trait SomeTrait<const LHS: usize, const RHS: u32>
where
LHS: < RHS,
LHS + RHS: 10,
{ Const generics are a fine incremental improvement to the language, I have a strong opinion that it is no substitute for true compile-time, type-system-only, types-as-values utility. |
I understand. My proposal was that Pasted here: #![feature(generic_const_exprs)]
use typenum::*; // 1.17.0
trait HList {
const SIZE: usize;
}
trait HWApi<N> {
fn config<L>(hlist: L) -> ()
where
L: HList,
Const<{ <L as HList>::SIZE }>: IsEqual<N>;
} |
I wasn't familiar with that as an option. I can see it coming in handy, but I still hold the view that there is merit to type-level expression of length being a core-feature of an HList construct. An
vs not restricting to the
It makes sense to me to retain the number-component of an HList type-construction. I can also see it making sense to keep the The value of adding Also, there might be corner cases where the utility of the type-len is diminished by relying on As for the issue with typenum being an additional dependency, I'll have a look at making it a feature-gated dependency. Without that, how do you feel about re-exporting the typenum crate alongside HList? Next update of this PR will include restoration of the |
I've updated with the following changes:
...that last point is a bit hacky. It needs a nightly feature to do it, but I don't want to turn this into a crate that needs a nightly build, especially if it's just for documentation. To this end, I added a nightly feature, and anything that needs nightly is gated behand that: On my CI, I get a failure when building for thumbv6m-none-eabi: cargo check --target thumbv6m-none-eabi --no-default-features I get the same locally on my machine. Not exactly sure what's going on there. Will investigate and come back with findings. |
++ I like the new direction ( |
Big issues with the documentation. For me, I don't like the idea of This makes things a bit silly in CI, CD, the use of
of these, I'm leaning towards 3.
I also think 3 has a non-zero chance of smooth the way to resolving the CI issue. This CI fail also merits a discussion-issue regarding the std feature... |
I'm a bit confused about what you mean by "lie". To be clear are you saying that the compiler will error out if typenum is used w/o actually using the feature (as opposed to "warn", and succeed), telling the user to turn on the feature to resolve? If so, I think that is fine. I'm -1 on compiler simply warning and succeeding compilation. |
You are correct. I wrote "warn" when I meant "error". I'll go with opt 3 |
CI fixed on my end, and added typenum feature to the published docs. My main concern now is that with the typenum length, HList implementations need to add where-clauses that rely on that feature. As far as I know, you can't hide the where-clause behind a feature gate, and so duplicate implementations must be written, using #[cfg[(not](.... to choose between the two. this is not all that nice, and welcome suggestions to improve, but as it stands, it's a required compromise if we want to put the typed-length behind a feature gate. With that in mind, it might be worth adding some things to the CI work flow to run tests with/without the typenum feature, but I don't want to hack away at that too much, at least without your direction. |
@@ -1,7 +1,7 @@ | |||
[package] | |||
name = "frunk" | |||
edition = "2021" | |||
version = "0.4.2" | |||
version = "0.5.0" |
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...
#[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; |
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.
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
@@ -1433,6 +1486,26 @@ where | |||
} | |||
} | |||
|
|||
#[cfg(feature = "typenum")] |
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.
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 = "typenum")] | ||
assert_eq!(<HList![usize, &str, f32] as HList>::Len::USIZE, 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.
More testing needed?
...hold off on morging for a bit. I'm getting a feel for using it in practice. |
....there's a problem: impl<DCT: HList, P: SetDutyCycle, PinsT: HList> PWMPinSetter<HCons<u16, DCT>, HCons<P, PinsT>> {
fn set_pin(dc_list: HCons<u16, DCT>, pins: HCons<P, PinsT>) {
pins.head.set_duty_pct(dc_list.head);
PWMPinSetter::set_pin(dc_list.tail, pins.tail)
}
} ...that's pretty damn close to what I want, but I get this error: error[E0277]: cannot add basically, the way I enterpret it, is that each time you prepend a new head to an existing list, I've set it up so that My stratagy was to set it such that thoughts? |
This creates a version bump and upgrades the HList assossciated assosciated const LEN to an assostiated
type: typenum::Unsigned
Motivation is as follows: I have a trait that needs a fixed length heterogenous list, but the length is generic to only a few numbers. the non generic way of implementing it:
I the requirements for this library are for
HWApi[1, 2, 3, 4, 6]
and I'd like to be able to define it as so:...This is only possible at the type level when HList has a canonical expression of its length at the type level as well.