Higher-kinded types: the difference between giving up, and moving forward

While HKT alone probably won't solve the issue with the high-level abstractions like Functor or Monad, it has other uses that are arguably much more relevant for Rust and not nearly as much in other languages.

A big source of code duplication comes from the inability to abstract over mutability, ownership, and containers:

  • It's very difficult to write a function agnostic to mutability, so that's why you see foo(&self) -> &X and foo_mut(&mut self) -> &mut self all over the place.
  • More generally, it's hard to write functions that are agnostic in ownership: &T, &mut T, Box<T>, Rc<T>, Arc<T>. This issue shows up in GitHub - Kimundi/owning-ref-rs: A library for creating references that carry their owner with them.
  • And even more generally, it's hard to write data types that are container-agnostic, e.g. enum Wrapper<F> { U32(F<u32>), String(F<String>), ... }.

All of these require HKT for their full expressiveness (plus some syntactic sugar, perhaps). There's kludgy ways to work around these problems, but ultimately the lack of for<T> (equivalent in power to HKTs) causes all kinds of trait constraints to eventually bubble up to the end user, including any that might be private implementation details.

(The inability to abstract over lifetimes through HKT is not nearly as severe of a problem because for<'a> already exists and works, modulo bugs and ergonomics.)

6 Likes