9 ms·
I think it probably falls more into the category of removing a restriction rather than a new feature, but I'm still waiting on HKTs - not for traits (as a forme
by glittershark 6y ago
I think it probably falls more into the category of removing a restriction rather than a new feature, but I'm still waiting on HKTs - not for traits (as a former die-hard haskeller, I have been gradually converted to the side of "rust doesn't need a Monad trait") but for data types. There's a design pattern in haskell called "Higher-Kinded-Data"[0] where you parametrize a datatype on a type-constructor, and use that to generically define either partial or total versions of that data type- something like (in rust syntax):
struct Person<F> {
pub name: F<String>,
pub age: F<u32>,
}
where you can then have `Person<Option>` be a person that may or may not have all fields filled, and `Person<Box>` be a person with all fields definitely filled. This is something I find myself reaching for surprisingly frequently when writing rust, and I feel like it's a missed benefit of implementing higher-kinded types.
[0]: https://reasonablypolymorphic.com/blog/higher-kinded-data/ https://reasonablypolymorphic.com/blog/higher-kinded-data/
All that said, I'm really excited to have const generics land! Props to all the amazing work by withoutboats and the entire rust team.
- steveklabnik 6y agoI like and enjoy HKTs in other languages, but don't feel a super strong need for them in Rust. I know some other people differ. It is very unclear when, if ever, Rust will get HKTs proper. GATs cover a lot of the same ground. We'll see :)
- adamch 6y agoWow, higher-kinded data. I've never heard of that, but it would absolutely be useful. E.g. for converting JSON inputs, where any field might be missing, into a version of the data which definitely has values for all the fields. Or a version of the struct where the values are actually read from a cache/database.
- glittershark 6y agoyes! once you use it you start wanting it everywhere, which is a big part of why I wish I could do it in Rust
- twic 6y agoI proposed using it to model lifecycles of entities: https://github.com/tim-group/higher-kinded-lifecycle/blob/master/src/main/scala/Idea.scala https://github.com/tim-group/higher-kinded-lifecycle/blob/ma... (in this code, "idea" is a domain concept from the firm i worked for at the time - basically a recommendation to buy a stock, which is 'opened' on a certain date, and 'closed' when it no longer seems like a good recommendation) My collegues didn't like it, and stuck to using separate types for objects in different stages of the lifecycle!
- IggleSniggle 6y agoTypeScript (as you might expect from a lang built for dealing with JS Objects), has really great tooling for this kind of type narrowing these days (including on string template matching now) and I miss it so much now that I’m writing Go.
- meetups323 6y agoIn TS this would be flipped: type Person = { name: string; age: number; } type SomeFieldsMissing = Partial<Person>; type AllFieldsRequired = Required<Person>; // No real change in this case Where Partial and Required are defined like: type Partial<T> = { [P in keyof T]?: T[P] | undefined; } type Required<T> = { [P in keyof T]-?: T[P]; } What would you think of something like this ("mapped types")? They can get fairly powerful: https://www.typescriptlang.org/docs/handbook/2/mapped-types.html https://www.typescriptlang.org/docs/handbook/2/mapped-types....
- kazoomonger 6y agoWhat would happen if I wanted only some fields to be required? To borrow from the GP example, something like this: struct Person<F> { pub name: F<String>, pub age: Option<u32>, } With `Person<Box>` still leaving `age` as optional.
- wizzwizz4 6y agoAdd an extra generic parameter. (Also, it shouldn't be Box; you'd want a bog-standard generic newtype for this, not an extra heap allocation.)
- glittershark 6y agoyeah, I picked Box mostly as a pedagogical instance of the identity functor that already exists in the stdlib - in the real world you'd definitely want `struct Identity<F>(F)` instead.
- meetups323 6y agoYeah there's going to be tradeoffs either way, the question is how often they come up in the situations the language is designed to target (versus random examples designed to showcase some obscure corner of PLT)... In this case for TS you might craft your own mappings that work like AllButXOptional<Person, 'age'>, or similar. So it's controlled externally versus internally, which who knows if thats better or not. One benefit of the TS approach is that it's a bit more composable... you can imagine things like OptionalIfRequiredInOther<ComplexPerson, SimplePerson>.
- michael_j_ward 6y agoThis might be of interest to you (this is the second article in the series, and they've been good so far) https://rustyyato.github.io/type/system,type/families/2021/02/22/Type-Families-1_5.html https://rustyyato.github.io/type/system,type/families/2021/0...
- viraptor 6y ago> where you can then have `Person<Option>` ... This is something I find myself reaching for surprisingly frequently What's a practical use case for it? It's cool in theory, but I struggle to come up with something that doesn't use at most 2 implementations (just copy the struct then), or some API where the types don't mix and can be generated via a macro instead.