Idea: List traits using const-generics

The use case in mind is dealing with tuples, however it applies to arrays as well.

Most of these are just normal syntax, things that needs magic are with comments;

trait Component<const Idx: usize> {
     type Output;
     fn get(&self) -> &Self::Output;
     fn get_mut(&mut self) -> &mut Self::Output;
}

trait Sequence<const Length: usize> : 
       Component<{0}>..Component<{Length}> /* <- compiler magic */ {
       fn get_component<T, const I: usize>(&self)
           -> Option<&dyn Component<I, Output=T>>;
       fn get_component_mut<T, const I: usize>(&mut self)
           -> Option<&dyn mut Component<I, Output=T>>;
       /* more reflection-like methods here */
}

/* compiler autogenerates Sequence impls for arrays and tuples*/

Problem 1: Refering to all tuples. This is quite easy:

trait List {
      const Length: usize;
      fn as_sequence(&self) -> &dyn Sequence<Self::Length>;
      fn as_sequence_mut(&mut self) -> &dyn mut Sequence<Self::Length>;
}

impl<T, const L: usize> List for T where T: Sequence<L> {
      const Length: usize = L;
      fn as_sequence(&self) -> &dyn Sequence<Self::Length> { self as _ }
      fn as_sequence_mut(&mut self) -> &dyn mut Sequence<Self::Length> { self  as _}
}

and now impl List stands for all tuples and arrays. this can be used for variadic parameters and so on.

Problem 2: Tuple comparison. This doesn't need a surface syntax if compiler want to do total magic. But if a surface syntax is desired, it can be something like this:

impl<const L: usize> PartialEq for T where T: Sequence<{L}>,
    {i @ <T as Component<0>>..<T as Component<L>>}::Output: 
         PartialEq<<T as Component<{i}>::Output> {
    fn eq(&self, rhs: &Self) -> bool {
        for i in 0..l {   /* a magic for loop here */
            self.get_component<<T as Component<i>>::Output, {i}>().unwrap() ==
            rhs.get_component<<T as Component<i>>::Output, {i}>().unwrap()
        }
    }
}

Problem 3: Clone and Default. I think can be done using the new MaybeUninit functionalities.

impl<const L: usize> Default for T,
    {<T as Component<0>>..<T as Component<L>>}::Output : Default {
    fn default() -> T {
        let mut v = MaybeUninit::<T>::uninit();
        for i in 0..l {   /* a magic for loop here */
            let v: MaybeUninit<<T as Component<i>::Output> = 
                /* somehow get a maybe uninit for that component here*/;
           v.write(Default::default()); 
        }
        v.assume_init()
    }
}

This is : for<const N...> Component<N> and a Fin encoding (see https://github.com/idris-lang/Idris-dev/blob/master/libs/base/Data/Fin.idr).

3 Likes

There is no need to add language features to do that, assuming specialization is available.