[−][src]Crate higher
This crate implements a mechanism to fake higher kinded types in a limited way in Rust with a minimum of boilerplate.
But Why?
If you ever had a trait T<A>
and wanted to swap the type parameter out in
a generic way to produce a type T<B>
in the trait's type signatures,
there's no straightforward way to do this currently in Rust, because this
would require Rust's type system to support higher kinded types. It can deal
with T<A>
but it can't deal with the concept of just T
where the type
parameter is yet unspecified (what's known as a type constructor - think of
it as a function which takes a type parameter, like A
, and returns a
concrete type T<A>
).
higher
provides the trait Lift
which, when given a type T<A>
which
implements Lift<A, B>
, will let you derive the concrete type T<B>
. This
needs to be implemented for any T<A>
which needs Lift
ing, but the
higher-derive
crate provides a custom derive for it, so
that you can quickly add it to your own types without boilerplate:
#[derive(Lift)]
enum MyLittleOption<A> {
Definitely(A),
NotReally,
}
Now, to convert from T<A>
to T<B>
, you can get the Target1
associated
type out of the Lift
trait:
<MyLittleOption<A> as Lift<A, B>>::Target1
// this resolves to MyLittleOption<B>
There is also the Lift3<A, B, C>
trait, which is also generated by the
Lift
derive, which lets you go from T<A>
to both T<B>
and T<C>
in
one go. This is useful if you need an intermediate type in one of your type
signatures, for instance a zip or merge function: Fn(T<A>, T<B>) -> T<C>
.
Here is how to use the above example type with Lift3
:
<MyLittleOption<A> as Lift3<A, B, C>>::Target2
// this resolves to MyLittleOption<B>
<MyLittleOption<A> as Lift3<A, B, C>>::Target1
// this resolves to MyLittleOption<C>
// the numbers go from right to left, so Target1 = C and Target2 = B.
Further, there's the Bilift<A, B, C, D>
trait, for when you have two type
parameters to generalise over. This one takes you from T<A, B>
to T<C, D>
. There's no corresponding Bilift3
trait as yet, because I haven't
found a practical need for it yet.
There's a corresponding derive for Bilift
:
#[derive(Bilift)]
enum MyLittleResult<A, E> {
Grand(A),
NotGrand(E),
}
And, to get the derived type out of the Bilift
:
<MyLittleResult<A, B> as Bilift<A, B, C, D>>::Target1
// this resolves to MyLittleResult<C, D>
Yes, But Why, Really?
Because sometimes one just gets homesick for Haskell and wants to implement
the Functor
hierarchy. You'll find this in the higher-cat
crate. It's
not really very suited for writing good Rust code, but it makes Haskell
programmers feel happy and it has a lot of funny words.
Traits
Bilift |
|
Lift |
|
Lift3 |
|