Live data from Hacker News

Rust: “Explain GATs Like I'm 5 Years Old”

old.reddit.com

131–140 of 193 posts

Re: Rust: “Explain GATs Like I'm 5 Years Old”

#131
post #111

Earlier quoted context omitted.

Not trying to make you feel like you have failed, but whenever I encounter language features on this level - aka not comprehensible for a mortal like myself - I do tend to want to shamefully turn away from this profession entirely. The good news is that I am most likely a total dunce. I wish I could become reasonable with rust because I do like the concepts that I think I grasp.

When I find a language feature I don't understand even after reading what's out there, I ignore it. Then I plod along with my own code. Eventually I'll notice I've been writing the same boilerplate code again and again, and I'll finally see the use for the feature. At that point it's less a matter of "understanding" in a grand intellectual sense, and more just fitting my needs to the syntax. After a few such instance…

Indeed, when I was very first learning to code I did this with arrays! Couldn’t understand the advantage of writing foo[0] and foo[1] over foo0 and foo1. And in some cases there isn’t really one. But of course arrays are very useful in general.

Re: Rust: “Explain GATs Like I'm 5 Years Old”

#132

Earlier quoted context omitted.

These are called sub-types or pattern types, and they are not in the language. But they are something I would love to have. You can’t emulate them with GAT unfortunately. https://cohost.org/oli-obk/post/165584-ranged-integers-via

I've not heard this called sub- or pattern types before. Perhaps the rfc syntax is complicating the discussion. We have struct and tuple in Rust where tuple is an ad-hoc/anonymous struct where only the number and order of member types matter. We have enum which are nominal types. We don't have the ad-hoc/anonymous version of them where only the set of possible types matter. In TypeScript, F#/OCaml they would be like…

I first heard about this feature in OCaml. I got the name subtype or pattern type from this thread:

https://internals.rust-lang.org/t/thoughts-on-pattern-types-...

Re: Rust: “Explain GATs Like I'm 5 Years Old”

#133

The top Reddit example is tremendous. I'm passed being a Rust beginner, but I have a note to the Rust community: Your official examples are overcomplicated and bad and you should also feel a little bad. Stick to things like apple, orange, pear and you'll see much easier adoption than if you go with something like LendingIterator! Through the GAT process all I have seen is the same hyper-specific example used that con…

> Because everyone likes to use that LendingIterator example I would offer a bit non-trivial example

Re: Rust: “Explain GATs Like I'm 5 Years Old”

#134

Earlier quoted context omitted.

I hope you don't. To establish my credentials for this comment - GATs were my idea. The entire point of GATs was to carve out a design space that solved peoples' problems without being so high-minded as monads and HKT and so on. Unfortunately, a lot of people who like monads like to talk about GATs in the same way. But its really as simple as this: when you add an associated type to a trait, you can make that type ge…

Not trying to make you feel like you have failed, but whenever I encounter language features on this level - aka not comprehensible for a mortal like myself - I do tend to want to shamefully turn away from this profession entirely. The good news is that I am most likely a total dunce. I wish I could become reasonable with rust because I do like the concepts that I think I grasp.

You're begging the question when you say that GATs are "not comprehensible for a mortal like myself." My entire point is that they were designed not to be incomprehensible.

What is "not comprehensible for a mortal" in this situation is why adding a generic to an associated type is a long-anticipated feature that took years of development to support. This is related to the "incomprehensible" discussion that occurs around this feature, talking about type functions and higher kindedness and all of these mathematical formalisms. But this discussion is just happening among practitioners and enthusiasts schooled in a certain jargon and area of arcane knowledge. It doesn't mean you need to understand any of this to just use the feature.

It's like people saying the internet is "not comprehensible for a mortal like myself" because most people do not have the background to properly understand IP/TCP/HTTP and how things like this undergirdle the technology that they use. And yet they use the internet just fine. If the design has succeeded, you should not need to even hear words like "higher kindedness" before you can make your associated type generic.

Possibly the system design is imperfect and the abstractions leaks and you as a user have to learn more than one would hope in order to successfully use the tool to accomplish your goals. Rust doesn't have a perfect track record here.

Re: Rust: “Explain GATs Like I'm 5 Years Old”

#135

Earlier quoted context omitted.

this sounds almost too easy to understand, will have to check with the documentation. (thank you!)

I think 90% of the reason why people have a hard time with GAT is because there aren't a ton of use cases. From what I can tell, GAT is quite simple - it is just like all other Rust generics except that now the generic can be in a new place. Rust has made it years without needing this (granted, a few things have been less than ideal because of it) because, for the most part, people have not needed to reach for this t…

It's needed for a lot of features people desperately want. Async functions in traits, closures that return futures where you need to run the future in a loop, and array indexing that doesn't necessarily return a reference, are three examples of things that essentially need GATs in order to be implemented without allocating. You may not feel like you missed it but not having that leads to a ton of ugly workarounds in Rust code. Also, in a lot of cases where people would otherwise use GATs, people instead work around them by making traits generic and have super complicated towers of generic types, you've probably seen interfaces like this when using hyper for instance. GATs would simplify most of those interfaces.

Re: Rust: “Explain GATs Like I'm 5 Years Old”

#136

Earlier quoted context omitted.

The part that says `T `. GATs don't allow you to do that, not directly. GATs allow you, as said by Boats in https://news.ycombinator.com/item?id=33506540 , have a generic type as an associated type, nothing less, nothing more. If you want to express `T `, GATs _enables_ you to do that as a kind of a distorted encoding, but it's not as simple as https://news.ycombinator.com/item?id=33505810 makes it seem.

Bring able to abstract over Rc/RefCell and Arc/Mutex with marker types seems pretty useful. It's not directly HKT but it enables use cases that would otherwise be supported by HKT.

Yes, but that doesn't make it somehow accurate to explain HKT and pretend you're explaining GAT...

Re: Rust: “Explain GATs Like I'm 5 Years Old”

#138

Earlier quoted context omitted.

Bring able to abstract over Rc/RefCell and Arc/Mutex with marker types seems pretty useful. It's not directly HKT but it enables use cases that would otherwise be supported by HKT.

Yes, but that doesn't make it somehow accurate to explain HKT and pretend you're explaining GAT...

That is definitely true.

Re: Rust: “Explain GATs Like I'm 5 Years Old”

#139
Let me try to explain GAT from a C++ programmer perspective:

'int' is a type. 'vector' is also a type

but 'vector' itself can be understood as a function from type to type (for example, invoking vector with an int produces vector)

Now let's say you want a function that given a type produces a vector of pairs of that type. In C++ you could do:

   template
   struct pair_vector : vector > {};
But using inheritance for this is not great, the result of pair_vector is really not a vector >. You can use an associated type:

   template
   struct pair_vector { using result = vector>; };
now 'pair_vector::result' is exactly 'vector >' [1].

So these are first order type functions. Let's say you want higher order type functions, i.e. functions that take other type functions as parameters or return them.

For example let's say you want a to build a pair piece wise: you want a function pair_1st that given a type X returns another function that takes a type Y and finally returns a pair:

   template 
   struct pair_1st { 
      template
      struct apply { using result = pair; };
   };
So: 'pair_1st::apply::result' is 'pair'; 'pair_1st' is an higher order function from type => (to a function form type => type).

Apply here is an associated type, (like result before), but is generic, hence an associated generic type.

So, what if we also want to abstract over pair in 'pair_1st', i.e. an higher order function that takes two parameters, the first a function from type => to type, and the second a type:

   template F, class X> struct bind_1st {
      template
      struct apply { using result = F; } 
   };
Now 'bind_1st::apply::result' is again pair;

I think that rust doesn't allow higher order (i.e. template template) parameters, but that's not a big problem:

   template struct bind_1st {
      template
      struct apply { using result = F::apply::result; }  [2]
   };

   struct pair_fn { 
         template struct apply { using result = pair; };
   };
By lifting pair into into a pair_fn class with a GAT we can solve the issue: bind_1st::apply::result is again the same as pair. In fact in C++ for a long time, before variadic templates, even if template template parameters were available from the beginning, GATs were the preferred way to encode higher order type functions.

The big difference between rust and C++ of course is that the type level functions in C++ are untyped other than maybe being able to specify the arity, while rust has a proper type system for generics, which I assume make all of this more complicated to implement from a compiler point of view.

Now why would you want all this nonsense outside of hardcore type level programming? Well, there is a continuum from simply writing a generic class to actual metaprogramming, so even if you do not want to do the latter, you might end up using on some of this stuff even for relatively simple generics.

[1] we can use template using to hide ::result, but that's a c++ quirk which is not important in the grand scheme of things.

[2] template typename noise removed for sanity

Re: Rust: “Explain GATs Like I'm 5 Years Old”

#140
post #40
post #30

Earlier quoted context omitted.

GATs are an essential part of Rust's long-term async/await story. It's not language-wankery, this is driven by actual real-world use cases. Rust's own stdlib would have benefited from this in many places had this feature existed years ago.

No, they're not. GATs are used internally in the compiler to implement async methods, but there is absolutely no need for them to be in the language for that (just like generators are used to implement await but are not part of the (stable) language). GATs may or may not be useful, but IMO the actual real-world use cases are pretty weak.

Even if the use cases outside the standard library were weak, given that the work needs to be done anyway for the standard library, why wouldn't you expose them?
Post reply on HN