I hope there is Higher-kinded Type

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