-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Higher kinded polymorphism #324
Comments
To fully support the idioms that HKP supports we would also need implicits, this would be also useful for passing the execution context to futures for example |
Can someone tell the status of higher kinded polymorphism in Rust? When will this feature be added to the language approximately? |
No one is currently implementing it, and no one has made a solid specification of what it would look like. If it happens, it will not be for a long time. |
Search keywords: higher kinded types, HKT, monad, functor. (cc #1185 (comment)) |
Why is no one working on this? |
Are there any status updates at this time? |
@antonkatz not yet. Lots of work is going into implementing the MIR/HIR RFCs, which enable more typesystem features by making the internal representations easier to operate on. |
@steveklabnik I wonder what it would look like. Do anyone have any ideas about how the semantics, design, and syntax would be? |
Nobody has come up with that stuff yet, no, or at least, no serious proposals. |
OK, let's talk about syntax as a light starter then. Looking at the proposals made so far for Swift in typelift/swift#1, I personally do not find any of them particularly appealing or suitable for Rust. Currently I would be leaning towards something like the following (using classic Trait declarationtrait<A> Functor {
fn fmap<B>(&self, f: Fn(A) -> B) -> Self<B>;
} Here
Also, HKT would only exist in the context of a trait declaration (e.g. a function outside of a trait could not have a return type of kind Trait implementationimpl<A> Functor for List<A> {
fn fmap<B>(&self, f: Fn(A) -> B) -> List<B> { ... }
} |
Cross-pasting from the duplicate issue I opened:
|
Also possibly related to @glaebhoerl's proposal: https://ghc.haskell.org/trac/ghc/wiki/DependentHaskell/Phase1 |
To me this looks confusingly similar to Alternate strawman: |
Wadler's law in action... |
@comex : yes that may be confusing at first, but I think it could make sense:
The pattern here is that, as the introduction of the generic type moves from right to left, the binding of that type parameter to a concrete type happens later and later. |
Having a decent syntax for expressing higher kinded traits is important, but I am sure there are more pressing challenges that need to be solved when it comes to figuring out the intricacies of semantics, and how these interacts with the existing features of the type system. Do we need to wait for a more solid, formal foundation for the type system, perhaps post MIR? How does the feature interact with type inference? How will libraries look once they can take advantage of higher kinded polymorphism? Can we have some interesting examples of what might be possible on the library front to help guide the design process? |
@bjz yes I think we definitely need to wait for MIR before we can have a decent idea of how this may be implemented and how it interacts with the rest of the type system, that's why I suggested to start thinking about (or just playing around with) syntax for the time being. If you think this issue is not appropriate to discuss that, I am happy to move the discussion somewhere else. Good point about providing examples of what would be possible with HKTs that is currently impossible or not ideal, it will be useful also to guide the discussion about semantics once we get closer to that. |
Yeah, having a good base of concrete examples might help guide syntax, and figure out desirable semantics. At least it is something that can be done in the mean time, and would really help with eventually crafting high quality RFC. |
A fairly simply, but IMHO interesting example is improving the API for iterators: With HKT, we could define the
This flexibility is needed, e.g., when writing a safe Doubly-Linked-List with |
One thing that's important is consistencies. HKT is a very powerful concepts, and will probably be used a lot when (if?) they're introduced. Having syntax and semantics which is consistent with Rust's type system is important for such a big change. Another subject to discuss is how such a change will affect the libraries. How will libstd look with HKTs? |
My humble opinion is that HKT is what brings Rust into a whole different league. Rust might just become a contender in the functional programming community. |
In general, higher-kinded polymorphism allows functions to be generic over a type constructors. And rust has many type constructors. :) As @RalfJung said, they allows functions to be generic over the specific pointer management technique, which suggests they might simplify the contentious situation around allocators. In Haskell, HTKs appear pretty central to handling errors with type constructors, so they might help simplify exception looking constructs in Rust too. Also, it appears HKTs would improve dealing with Rust's different types of closures too, which I suspect is what @antonkatz means. |
@ticki you're right. I probably don't. |
@antonkatz Right, HKTs might render big parts of the API obsolete, so sure adding HKTs is a big thing. But they provide a really nifty abstraction allowing very clean API. But I do believe the change is worth it. So sure, adding HKTs is a drastic move, but due to the power of them, it's worth the price. |
It sounds like we'd have to restrict this to object safe bounds and represent this the same as trait objects - that is |
@jmegaffin Wait. If you handle size/alignment dynamically then do you even need the "behind a pointer" restriction in the signature? I would think that "statically require every type-instantiation to have the same representation" ("behind a pointer") so that you don't need anything extra at runtime (i.e. analogous to the uniform boxed representation of most extant GCed languages), and "actually deal with all the statically-unknown representations at runtime instead" (so-called "intensional type analysis"), are two disjoint approaches. |
What about higher kinded lifetimes? Lifetime parameters are always erased, so the problems with runtime representation simply vanish. And the problem can be much harder to work around, often requiring unnecessary copying. |
RFC #1598 is merged
|
Has there been any recent movement around this issue? |
The foundational work in the compiler is being done to enable this. So not directly, but in a broad sense, yes. |
@steveklabnik any links to what work is ahead before HKTs would be directly possible? I tried searching around in the rust issues and rfcs but couldn't figure out what the status is. |
The answer depends on what you mean by "directly." Work is underway to implement generic associated types (GATs), described in RFC #1598 and tracked in this tracking issue: rust-lang/rust#44265 Generic associated types are capable of representing the same type relationships as the higher kinded type class polymorphism in Haskell (for example) - what is normally called "HKT". But its not exactly the same feature, its just equally expressive. There is no work underway on introducing a mechanism for polymorphism over higher kinded terms other than through generic associated types, and I don't currently see much impetus to add a separate faculty other than GATs. Niko Matsakis wrote a blog series exploring the relationship between GATs and HKT, the last post contains links to all the previous ones: http://smallcultfollowing.com/babysteps/blog/2016/11/09/associated-type-constructors-part-4-unifying-atc-and-hkt/ (he calls GATs "ATCs" in that series) EDIT: Looking at Niko's blog post again, I strongly recommend it. Its an excellent representation of our thinking about HKT in Rust right now. |
@skeet70 i was typing a response but @withoutboats just beat me to it; GATs are not exactly this but can provide similar things. "impl Trait in Traits" is also, in my understanding, equivalent to HKT. It is true that we have no immediate plans to offer HKT directly at the time. |
…ible Deprecate Component#isVisible
I'd love to see something like Archery baked into the language. It would allow people to easily and ergonomically abstract over Rc and Arc types. |
Is there no update or proposal regarding this after two years? |
Correct, those underlying features are still being worked on, and have not stabilized yet. Things take time. |
Emulating HKTs with GATs on nightly gives us a nice looking (incorrect due to not constraining the return type to #![feature(generic_associated_types)]
trait Monad {
type Start;
type End<B>: Monad;
fn bind<B>(self, f: impl Fn(Self::Start) -> Self::End<B>) -> Self::End<B>;
} It can be implemented for impl<A> Monad for Option<A> {
type Start = A;
type End<B> = Option<B>;
fn bind<B>(self, f: impl Fn(A) -> Option<B>) -> Option<B> {
self.and_then(f)
}
}
fn main() {
let monad = Some(1).bind(|x| Some(x * 2));
println!("{:?}", monad); // Some(2)
} |
Yes, but that definition of monad is not precise enough. You can implement a monad where |
I think family pattern allows one to express monad precisely, although it's verbose (playground): #![feature(generic_associated_types)]
trait MonadFamily {
type M<T>;
fn pure<T>(value: T) -> Self::M<T>;
fn bind<T, U, F>(this: Self::M<T>, f: F) -> Self::M<U>
where
F: Fn(T) -> Self::M<U>;
}
struct Maybe;
impl MonadFamily for Maybe {
type M<T> = Option<T>;
fn pure<T>(value: T) -> Option<T> {
Some(value)
}
fn bind<T, U, F>(this: Option<T>, f: F) -> Option<U>
where
F: Fn(T) -> Option<U>,
{
this.and_then(f)
}
}
fn play_with_monad<M: MonadFamily>() where M::M<i32>: std::fmt::Debug{
let x = M::pure::<i32>(1);
let x = M::bind(x, |x| M::pure(2*x));
println!("{:?}", x);
}
fn main() {
play_with_monad::<Maybe>();
} |
I think the biggest issue with that is that style (@MikailBag) is that you can't pass only one type parameter if there's more than one; otoh, doing C++ style tag structs: #![feature(generic_associated_types)]
trait Monad /* : Applicative (for pure/return, doesn't matter for this example) */ {
type S<T>;
fn bind<T, U, F>(_: Self::S<T>, f: F) -> Self::S<U>
where
F: FnOnce(T) -> Self::S<U>;
}
struct OptionMonad;
impl Monad for OptionMonad {
type S<T> = Option<T>;
fn bind<T, U, F>(opt: Self::S<T>, f: F) -> Self::S<U>
where
F: FnOnce(T) -> Self::S<U>
{
match opt {
Option::Some(x) => f(x),
Option::None => Option::None,
}
}
}
fn bind<M, T, U, F>(_: M, x: M::S<T>, f: F) -> M::S<U>
where
M: Monad,
F: FnOnce(T) -> M::S<U>
{
M::bind(x, f)
}
fn main() {
let x = bind(OptionMonad, Some(1), |x| Some(x * 2));
println!("{:?}", x);
} |
Just thinking about syntax, what about having HKTs represented as a bound on trait Monad where Self<T> {
fn return(obj: T) -> Self<T>;
fn apply<U>(&mut self: Self<Start>, f: F) -> Self<U> where F: FnOnce(T) -> Self<U>;
} |
Yeah, why is it that pub trait Void {
fn void(self) -> Self<()> {
todo!()
}
} |
@fosskers Because it's not a trivial feature to implement, no one has done the design work, and there are other accepted, more important rfcs to work on. GATs (available on nightly) represent most of the useful part of higher kinded types, and can be used to emluate real HKTs in a limited capacity via the family pattern: http://smallcultfollowing.com/babysteps/blog/2016/11/03/associated-type-constructors-part-2-family-traits/ |
Does any pr related to HKTs? Thx. |
Is there any progress so far? I would really appreciate and love to see some more information. ❤️ |
Issue by tiffany352
Monday Sep 02, 2013 at 01:14 GMT
For earlier discussion, see rust-lang/rust#8922
This issue was labelled with: A-typesystem, I-wishlist in the Rust repository
Rust doesn't support higher kinded polymorphism, despite being a common feature in many functional programming languages. As far as I know, it is a commonly requested feature from people in the FP crowd.
The text was updated successfully, but these errors were encountered: