I have a struct type DataUnvalidated
contains Option<T>
to represent the data sent from the network that haven't been validated, and also have a type Data
to store the validated-version of DataUnvalidated
.
After receiving and validating some Data
, I batch them to DataBatched
.
struct A;
struct B;
struct C;
struct D;
struct DataUnvalidated {
a: Option<A>,
b: Option<B>,
c: Option<C>,
d: Option<D>
}
struct Data {
a: A,
b: B,
c: C,
d: D
}
struct DataBatched {
a: Vec<A>,
b: Vec<B>,
c: Vec<C>,
d: Vec<D>
}
You can see that there is some duplication.
But if Higher-kinded Type is supported, we can abstract them as following:
struct AbstractData<F> {
a: F<A>,
b: F<B>,
c: F<C>,
d: F<D>
}
type DataUnvalidated = AbstractData<Option>;
type Data = AbstractData<Identity>;
type DataBatched = AbstractData<Vec>;
It's much more cleaner, isn't it?
This is possible in current nightly using the "generic associated types" feature. (It's a little more verbose than your example code, since you have to set a trait bound and refer to its associated type, but it definitely saves the duplication.)
2 Likes
If only the four types A
, B
, C
, D
(or generally, only a finite number of types) are required to be supported as arguments to the higher-kinded type for AbstractData
, then you can use a trait with generic argument, and just list the requirements. E.g. this compiles on stable:
#![allow(unused)]
struct A;
struct B;
struct C;
struct D;
struct AbstractData<F>
where
F: HKT<A> + HKT<B> + HKT<C> + HKT<D>,
{
a: Apply<F, A>,
b: Apply<F, B>,
c: Apply<F, C>,
d: Apply<F, D>,
}
trait HKT<T> {
type Applied;
}
type Apply<F, T> = <F as HKT<T>>::Applied;
macro_rules! hkt {
($Name:ident = |$T:ident| $ty:ty) => {
enum $Name {}
impl<$T> HKT<$T> for $Name {
type Applied = $ty;
}
};
}
hkt!(Option_ = |T| Option<T>);
hkt!(Identity = |T| T);
hkt!(Vec_ = |T| Vec<T>);
type DataUnvalidated = AbstractData<Option_>;
type Data = AbstractData<Identity>;
type DataBatched = AbstractData<Vec_>;
fn main() {
let _foo = DataUnvalidated {
a: None,
b: None,
c: Some(C),
d: None,
};
}
3 Likes
I just come here to mention that there's no such thing as an identity Higher Kinded Type such that Identity<u8> == u8
, HKTs are injective so that they can be inferred from concrete types.
1 Like