Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upHigher kinded polymorphism #324
Comments
nrc
added
the
postponed
label
Sep 25, 2014
This comment has been minimized.
This comment has been minimized.
larroy
commented
Dec 28, 2014
|
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 |
This comment has been minimized.
This comment has been minimized.
AlexanderKaraberov
commented
Jan 2, 2015
|
Can someone tell the status of higher kinded polymorphism in Rust? When will this feature be added to the language approximately? |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
Search keywords: higher kinded types, HKT, monad, functor. (cc #1185 (comment)) |
This comment has been minimized.
This comment has been minimized.
int-index
commented
Aug 21, 2015
|
Why is no one working on this? |
This comment has been minimized.
This comment has been minimized.
antonkatz
commented
Sep 11, 2015
|
Are there any status updates at this time? |
This comment has been minimized.
This comment has been minimized.
|
@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. |
eefriedman
referenced this issue
Sep 29, 2015
Closed
Higher ranked lifetimes cannot cross type boundaries #28721
This comment has been minimized.
This comment has been minimized.
|
@steveklabnik I wonder what it would look like. Do anyone have any ideas about how the semantics, design, and syntax would be? |
This comment has been minimized.
This comment has been minimized.
|
Nobody has come up with that stuff yet, no, or at least, no serious proposals. |
This comment has been minimized.
This comment has been minimized.
tiziano88
commented
Dec 7, 2015
|
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> { ... }
} |
This comment has been minimized.
This comment has been minimized.
|
Cross-pasting from the duplicate issue I opened:
|
This comment has been minimized.
This comment has been minimized.
tiziano88
commented
Dec 8, 2015
|
Also possibly related to @glaebhoerl's proposal: https://ghc.haskell.org/trac/ghc/wiki/DependentHaskell/Phase1 |
This comment has been minimized.
This comment has been minimized.
comex
commented
Dec 8, 2015
To me this looks confusingly similar to Alternate strawman: |
This comment has been minimized.
This comment has been minimized.
int-index
commented
Dec 8, 2015
|
Wadler's law in action... |
This comment has been minimized.
This comment has been minimized.
tiziano88
commented
Dec 8, 2015
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
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? |
This comment has been minimized.
This comment has been minimized.
tiziano88
commented
Dec 8, 2015
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
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? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
antonkatz
commented
Dec 8, 2015
|
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. |
This comment has been minimized.
This comment has been minimized.
|
@antonkatz I'm not sure you know how powerful HKTs are. Rust already got generics, but this is just a major addition to generics. |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Dec 9, 2015
|
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. |
This comment has been minimized.
This comment has been minimized.
antonkatz
commented
Dec 10, 2015
|
@ticki you're right. I probably don't. |
This comment has been minimized.
This comment has been minimized.
BardurArantsson
commented
Sep 25, 2016
|
I can only concur with @withoutboats ... I'm a Haskeller and my (current) thinking is that MPTC are entirely uncontroversial. (Not that MPTCs have much to do with HKTs, at least as far as I understand it) |
This comment has been minimized.
This comment has been minimized.
|
I vaguely agree with this sentiment, though:
I think adding more flexibility in the associated items of a trait is more important than adding more flexibility in parameter kinds. For example, I often want higher kinded associated items, but rarely want other forms of higher kinded polymorphism. I want associated consts to be stabilized more often than I want const parameters. I'm not saying more parameter kinds can't be beneficial, but I've found associated items to really be 'where its at' in my experience. |
This comment has been minimized.
This comment has been minimized.
|
As established by @pthariensflame, Rust already has MPTCs, with one example being the From trait. What Rust lacks atm is the ability to define type constructors of higher kindedness - i.e: For consistencies sake - if higher kindedness is added to the language, then it should be supported for MPTCs as well, i.e: more than for |
This comment has been minimized.
This comment has been minimized.
JohnColanduoni
commented
Oct 5, 2016
One important example I've come up against is parameterizing structures over different smart pointer types. Particularly at the library level, it is quite useful to have structures which use some kind of reference counting (Rc or Arc) but don't particularly care which. Allowing the user to choose gives us the best of both worlds: cheap reference counting for single-threaded scenarios, and atomic reference counting when Send + Sync are required. As for avoiding Haskell-style MPTCs, Rust's concept of "Kind" could be based on a sort of "trait signature" as opposed to a function from types to types. For example, you could have the kind of traits with N parameters and M type members. |
This comment has been minimized.
This comment has been minimized.
|
@JohnColanduoni I know I've used this kind of parametrizing in C++ in order to implement trait object alikes, and I'd like to be able to do the same thing in Rust. |
This comment has been minimized.
This comment has been minimized.
|
Crazy idea, taking the lead from the The following are builtins: trait Type {}
trait <impl Type> -> impl Type {}
trait <impl Type, impl Type> -> impl Type {}
...These are automatically implemented by the compiler like so: impl Type for () {}
impl Type for i32 {}
...
impl <impl Type> -> impl Type for Option {}
impl <impl Type, impl Type> -> impl Type for Result {}
...We also have type level closures: type MyResult = |T: impl Type| Result<T, MyError>;
type MyI32Result = MyResult<i32>;We now can define: trait Functor: <impl Type> -> impl Type {
fn map<T, U>(self : Self<T>, f: impl Fn(T) -> U) -> Self<U>;
}
impl Functor for Option {
fn map<T, U>(self : Option<T>, f: impl Fn(T) -> U) -> Option<U> { ... }
}
impl<E> Functor for (|T| Result<T, E>) {
fn map<T, U>(self : Result<T, E>, f: impl FnMut(T) -> U) -> Result<U, E> { ... }
}This sacrifices 'prettiness' in order to stay try to keep it consistent with Rust's existing concrete syntax, semantics, and idioms. For example, the |
This comment has been minimized.
This comment has been minimized.
skeet70
commented
Oct 19, 2016
•
|
Came across kinder, thought it might be of interest to you if you hadn't seen it already. From the README:
|
This comment has been minimized.
This comment has been minimized.
larroy
commented
Oct 27, 2016
|
I was thinking about this for some time and reached the conclusion that generics of a higher kind requires some kind of generic that is not monomorphised at compile time, ie. if you need to know the size of an object at compile time you can't really do it. Must be the reason why the languages that support this have some sort of runtime. Please correct me if I'm wrong. |
This comment has been minimized.
This comment has been minimized.
Boscop
commented
Oct 27, 2016
•
|
@larroy Many functional languages have HKT, even C++ has template templates. It doesn't require RTTI, they are monomorphized at compile-time. |
This comment has been minimized.
This comment has been minimized.
Types using higher kinded polymorphism can be monomorphized. On the other hand, I think higher rank types can't be monomorphized. On the other other hand, higher rank trait bounds with type parameters are fine. So its specifically a type like There are other people who would know better than me though. |
This comment has been minimized.
This comment has been minimized.
|
@withoutboats With |
Stebalien
referenced this issue
Nov 14, 2016
Closed
`len()` should be a member of a trait applied uniformly to all collections #1791
This comment has been minimized.
This comment has been minimized.
jmegaffin
commented
Dec 28, 2016
•
|
@larroy @withoutboats Implementing higher-rank types (and polymorphic recursion) requires no RTTI. You just have to make sure values of the quantified type are behind pointers, like Rust already does with exotically-sized types or trait objects. So we can say |
This comment has been minimized.
This comment has been minimized.
|
@jmegaffin That's interesting; how would they be represented? I'm not sure its worth the complexity cost but its cool. |
This comment has been minimized.
This comment has been minimized.
|
@jmegaffin Keep in mind that the existence of unsized types in Rust actually screws up that scheme in its naïve form, because of fat pointers. |
This comment has been minimized.
This comment has been minimized.
jmegaffin
commented
Dec 29, 2016
•
|
@withoutboats Not considering the fat pointer issue, pointers are fundamentally untyped. Thus, it would be represented exactly as written, plus pointers to vtables for any non-marker traits T is constrained to implement. Again, that doesn't take fat pointers into account. @pthariensflame Yeah, I suppose it works for e.g. Perhaps it was a mistake for exotically-sized types to bubble up information to their pointers. It's not strictly necessary, after all. |
This comment has been minimized.
This comment has been minimized.
|
It's not enough to make sure they're behind pointers in the fn signature, you also have to make sure they're not used in any way that's not behind-a-pointer in the function body either (copied to the stack, calling another function that's not "only-behind-pointers-safe", etc.). Unless you handle size/alignment completely dynamically... |
This comment has been minimized.
This comment has been minimized.
jmegaffin
commented
Dec 29, 2016
|
@glaebhoerl I don't see any issue with doing it dynamically. Performance will be a little worse, of course, but that's unavoidable. It's only not worse in languages like Haskell because they don't do any monomorphization in the first place. If you want to use a generic function polymorphically, you just compile it to be dynamic where necessary. Any generic functions that are called that depend on a polymorphic type must in turn be compiled to be dynamic and then used at that call site. That should be enough. |
This comment has been minimized.
This comment has been minimized.
|
It sounds like we'd have to restrict this to object safe bounds and represent this the same as trait objects - that is |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
DemiMarie
commented
Apr 10, 2017
|
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. |
This comment has been minimized.
This comment has been minimized.
tioover
commented
Sep 18, 2017
|
RFC #1598 is merged
|
Centril
added
the
T-lang
label
Feb 23, 2018
This comment has been minimized.
This comment has been minimized.
cruhl
commented
Oct 1, 2018
|
Has there been any recent movement around this issue? |
This comment has been minimized.
This comment has been minimized.
|
The foundational work in the compiler is being done to enable this. So not directly, but in a broad sense, yes. |
This comment has been minimized.
This comment has been minimized.
skeet70
commented
Oct 15, 2018
|
@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. |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
|
@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. |
rust-highfive commentedSep 25, 2014
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.