Notes on adding Constraint Kinds to Rust
a trait can be thought of as a type operator generating a "constraint" - what in Rust would usually be called a bound. For example:
// Declares a new item `Foo` with kind `type -> constraint`
trait Foo { }
// T: Foo applies Foo to `T`, `(type -> constraint)(type) => constraint`
fn bar<T>() where T: Foo { }Constraints are facts that have to hold true when the generic item is instantiated to a concrete item.
Haskell has an extension which lets you talk about constraints as a first class language feature. This gist is abound a natural way to extend Rust to support some way of talking about constraints, with the context of Rust's syntax (which is very different from Haskell).
Mainly we handle this by dealing with type -> constraint objects, rather than constraint objects directly.
trait Foo {
trait Bar;
fn baz<T: Self::Bar>(arg: T);
}
fn quux<T>() where T: Foo + <T as Foo>::BarAn associated trait is an associated item of the kind type -> constraint. You can then use it as a bound elsewhere
fn foo<T, trait Trait>() where T: TraitYou could also parameterize items by traits, having the kind type -> constraint, to use them in bounds
meta trait Monoid {
const EMPTY: Self;
fn append(lhs: Self, rhs: Self) -> Self;
fn concat(elems: Vec<Self>) -> Self {
elems.into_iter().fold(Self::EMPTY, Self::append)
}
}
impl Monoid for Add + Zero {
const EMPTY: Self = Zero::ZERO;
fn append(lhs: Self, rhs: Self) -> Self {
lhs + rhs
}
}
fn noop<T, trait M>(arg: T) -> T where T: M, M: Monoid {
T + <T as M>::EMPTY
}
// mappend::<Add + Zero> adds 2, whereas mappend::<Mul + One> doubles it
fn mappend2<trait M>(arg: i32) -> i32 where i32: M, M: Monoid {
M::append(arg, 2)
}The use of Self here is problematic, still need to figure out the syntax around that.
We won't do this any time in the near future, its just interesting to think about.
I wonder if we can use
Self::Selfto refer to the concreteSelftype, thus being able to distinguish it fromT: SelfandSelfas a trait