20 ms·
Monads and GATs in Nightly Rust
- bsaul 6y agoSide question : Is there any example of a language that has successfully added HKT after it was released and became popular ?
- littlestymaar 6y agoI'd say no, given that no language with HKT has ever been popular (sorry Haskell, you're cool, but not “popular”)…
- ivanbakel 6y agoI was going to object, but in doing my own research I found that Haskell ranked even below Dart in the SO Developer Survey for languages in use - and I certainly wouldn't consider Dart popular, since I've never seen, let alone met, anyone using it. That said, "popular" is a very contextual term. Within certain classes of programmers (and I don't mean the obvious tautological one) Haskell is extremely popular, and fit for purpose. I assume the same is true for Dart. And I would add that, in those circles, the introduction of advanced HKT features like RankNTypes has been extremely successful.
- yen223 6y agoDart is seeing a bit of interest thanks to Flutter, the cross-platform UI toolkit.
- gpderetta 6y agotemplate template parameters and nested template classes work as HKT in C++ in practice (and are extensively used for exactly that purpose), and I think C++ counts as a popular language.
- hurril 6y agoWould you consider Scala? It wasn't wildly popular before 2.8 (which is when HKTs were added) but not unpopular either.
- Lermex 6y agoI was surprised that to learn that HKT weren't part of Scala from the beginning and had to look that up. You're a bit wrong about when that happened: it was Scala 2.5 in 2007, not 2.8 which was released in 2010.
- RcouF1uZ4gsC 6y agoC++ did during the standardization to C++98 with template template parameters. C++ before 1998 was a very popular language, maybe even more popular relatively than it is now.
- jokethrowaway 6y agoI don't think they make or break a language for 99% of the developers. You can get 90% there without HKT and a similar developer experience. Current rust has some implementations which are simil-functors and simil-monads. This is just polishing the language by lifting a restriction.
- valenterry 6y ago> I don't think they make or break a language for 99% of the developers. True > You can get 90% there without HKT and a similar developer experience. Not really though. Unless similar developer experience means you suffer like hell and still have to deal with runtime errors and worse performance.
- Philip-J-Fry 6y agoIt seems that Rust just keeps getting more and more features. Do people generally feel like the more features the better the language? I'm personally of the opinion that less is more. Is this a pain point at the moment for Rust devs? Do you feel like the code you write is the same code 99% of other Rust developers would write to solve the same problem? Or is there actually a really large variety of styles?
- sfvisser 6y agoI'm not sure you can only see this in the light of 'more features'. From a more fundamental perspective GATs just lift a restriction. Rust already has generics and Rust already has associated types. Previously you needed to know/remember those features cannot interact for some reason, now they can. To me this is in a sense 'less' and not 'more'. So, a matter of perspective I guess.
- Philip-J-Fry 6y agoAh okay, that makes sense. Thanks
- The_rationalist 6y agoBut why have associated types in the first place since they have the same expressive power as generics? https://stackoverflow.com/questions/33647337/what-is-the-difference-between-generics-and-associated-types https://stackoverflow.com/questions/33647337/what-is-the-dif...
- Twisol 6y agoEven in Java, I'm rather sick of passing around types with `<X, Y, ?, ?>` with two honest-to-goodness generic types and two actually-chosen-by-the-implementation generic types. There's no good way in Java to hide those visible wildcards without making a wrapper class that only exposes the meaningful ones. Associated types make the distinction far clearer. They better capture the qualitative distinction that "you can choose these, I get to choose those". Also, associated types are inherently "functionally dependent" in the sense of Haskell's multi-parameter type classes. If you have a trait `F<X, Y>` with an associated type Z, you know that given X and Y, Z is fixed. Without associated types, `F<X, Y, Z>` could have multiple legal implementations for the same X, Y and different Z. This is extremely meaningful in languages like Haskell and Rust which implicitly thread around the trait methods. Typeclasses in Haskell can be imagined as describing concrete dictionaries of functions over the given types. Languages like Java (manually) and Scala (automatically, using "implicit") reify these typeclasses as dictionaries that are threaded through functions as extra parameters. You can often define multiple implementations of a given trait for the same types, and you get to choose which implementation to pass along. Rust and Haskell assert that traits have a single implementation for a given batch of types, and they automatically look up the correct implementation given the types you've specified. These "functional dependencies" mean that, in my example of `F<X, Y>` with associated type Z, it's sufficient to state what X and Y are to find the right implementation of F -- Z doesn't contribute to the lookup. If you don't have associated types, you could have multiple implementations that simply vary on Z, so you have to tell the compiler explicitly which one to use (by stating what Z is).
- dragonelite 6y agoHow does one get familiar with these kind meta programming concepts. Is there some kind bible or series one can read to get up to speed?
- Milner08 6y agoIm a Scala fan, so all these will be focused around Scala, but they will teach you about Monad and Category Theory. I would start with http://learnyouahaskell.com http://learnyouahaskell.com - Its Haskell based but it covers them pretty well. Otherwise I use https://typelevel.org/cats/ https://typelevel.org/cats/ for Scala daily and they have an _ok_ guide. But the best guide I've found for it was http://eed3si9n.com/herding-cats/ http://eed3si9n.com/herding-cats/
- dragonelite 6y agoLearnYouAHaskell bought the book last year but because of lack of time didn't worked through it. I should dust the book off in januari and give a read.
- jcora 6y agoDo note this won't teach you actual category theory (and shouldn't)
- deleted 6y ago[deleted]
- monoideism 6y agoMany Haskell and functional Scala books will give you a good understanding of type-driven development, but I'd recommend "Functional Scala" by Chisano/Bjarnson (and do the exercises). Alternately, "Learn You a Haskell" will provide a decent base. Either of these will give you the base needed to explore higher-level concepts that can easily be used in Haskell, Scala, Purescript, OCaml, a few statically-typed functional languages, and to some extent, Rust.
- 6y ago
- demurgos 6y agoDoes anybody know if GATs would enable to issue indexes/handles tied to a container without the use of closures? This would allow compile-time checks that indexes are only used with their container. The most advanced crate for such use-case is `indexing` [0] but it is pretty complex inside, requires closures and seems more like an experiment. [0]: https://github.com/bluss/indexing https://github.com/bluss/indexing
- Twisol 6y agoIn principle, the ingredient you need for that use case is "existential types". I suspect GATs let you define existential types -- after all, the selling point is that the trait defines some type, and you can't rely on what type any particular implementor chooses. (But I can't quite put my finger on why non-G associated types shouldn't be enough.) In Java(!), I have a class `Database<$Database>` whose constructor is private and whose sole static factory returns `Database<?>`. Thus, the caller must assume that every instance they create is parameterized over a distinct unknown type -- even if, in the implementation, $Database = Object. The dollar-sign prefix is a hint to the reader that this type parameter is meant to be a type correlated with a unique value. You can get an `Index<$Database>` from any database, and it's parametrized over the owning database's unique existential type. Indexes over two different databases can't be confused. The only issue (so far) is that you have to be careful not to "forget" the correlation between the type parameters on an index and its owning database. If the type parameter decays back into a wildcard, the correlation is lost. [1] https://varkor.github.io/blog/2018/07/03/existential-types-in-rust.html https://varkor.github.io/blog/2018/07/03/existential-types-i...
- olodus 6y agoI like that the article starts with describing Functor as "something almost every developer knows about". That might be a bit of a stretch. I would say almost all developers has interacted with what could be called a functor, but knowing the concept is something different. In certain areas it is starting to get well known though.
- contravariant 6y agoI agree, though I think developers should be aware of type-parameters, and should be aware that if A is a subtype of B then List[A] should be considered a subtype of List[B] (which turns List into a functor on types by inclusion). In fact I reckon almost every developer is aware of these things but perhaps doesn't realise the underlying pattern.
- Twisol 6y ago> if A is a subtype of B then List[A] should be considered a subtype of List[B] Well, careful -- I'm sure you know this by your name, but the naive treatment of Java's native array type as A[] <: B[] when A <: B leads to well-known soundness issues. Fundamentally that's because you can both read and write to Java's arrays; if you assume arrays are read-only then A[] <: B[] is indeed sound.
- contravariant 6y agoYeah I was thinking of the read only kind, you do need to be a bit careful when your objects are mutable ;-)
- kzrdude 6y agoAs has been noted in other forums, this article does not cover implementing Functor/Monad for the Option type family (for example) faithfully. What's missing is implementing Functor for `Option` (the "unapplied" option, without a type parameter), in such a way that it is evident in from the trait implementation that if you pass an Option to `fmap`, then you get an Option out (not just any Functor). In other words, that the particular flavour of functor or monad is preserved in these operations. Proof of concept code for this exists, but Rust doesn't support it in a fluent way, even with the GAT feature.
- Twisol 6y ago> What's missing is implementing Functor for `Option` (the "unapplied" option, without a type parameter), in such a way that it is evident in from the trait implementation that if you pass an Option to `fmap`, then you get an Option out (not just any Functor). I was momentarily confused by this explanation, so allow me to distill the problem. In the trait declaration for Functor, nothing requires that `<X<T> as Functor>::Wrapped<T> == X<T>`. In other words, the implementing type can be different from the return type of `map`. You would want to return `Self<T>` from `map`, but `Self` refers to the fully-reified type, which is to say the implementor has already decided what the type parameter (if any) is, and you as the trait author have no control over it. You need some way to force the above equality, which is probably what the referenced "proof of concept" does. (I think I found it here: https://github.com/edmundsmith/type-plugs https://github.com/edmundsmith/type-plugs) (EDIT: I found another proof of concept via the author's bug report. The two approaches seem pretty much the same, and it doesn't look too difficult to understand. https://users.rust-lang.org/t/monads-in-rust-with-gats/50487 https://users.rust-lang.org/t/monads-in-rust-with-gats/50487) The article does mention this problem explicitly: >> Interestingly, we have lost the knowledge here that Self::Wrapped<T> is also a Pointed. That's going to be a recurring theme for the next few traits.
- Nullabillity 6y agoAdding that restriction would make it impossible to implement Functor for lazy collections (for example, `<I as Iterator>::map()` returns a `Map<I, F>` where F is usually some unmentionable closure type).
- WhoCaresLies 6y agoThis confirms my concerns.. Rust is becoming a Scala language, too much features.. Rust should have been as simple as C, i wonder if there will be some compiler switchs to ban certain features, and crates that are tagged to work with certain features, so at least things will be easier to deal with
- dragonelite 6y agoYou do know you don't have to use everything under the sun. If you want to use a sub set then use the sub set.
- dboreham 6y agoWithout tooling (a kind of lint rule I suppose) to prohibit the use of advanced features, this is difficult to achieve in multi-developer projects.
- swsieber 6y agoYou can write custom pints if you're willing to build a custom toolchain (shouldn't be too terrible to rebuild it once a release) That said, GATs fix a real pain point not actually shown in the article. And that's for impl return types in trait methods. You can have return impl types in normal functions, and even associated methods, just not in trait methods. I think the term for this is existential types. And they occur a lot with async. So async in traits is a real pain point this feature fixes.
- alkonaut 6y ago"Using" a programming language is 99% reading and understanding code and 1% writing code. I only control the 1% i write and not the 99% I read. You can't know everything but you need to know enough to understand most of the code you read. If a feature becomes popular among others, then you need to understand it, even if you never use it yourself.
- cle 6y agoIt's never this simple. You have to decide what subset to use, understand how that subset could interact with the avoided language features, have a strong team culture that can agree on the subset and have the discipline to commit, understand how new language features affect that commitment, etc. In addition to needing to read and interact with other libraries that probably use it anyway. The team aspect alone is a deal breaker for me--when you have a technology that attracts people who are intrinsically interested in the technology, good luck trying to get them to avoid certain parts of it. That's a losing battle.
- appleflaxen 6y agoGAT = Generic Associated Type
- mabbo 6y agoThank you. I spent 10 minutes fruitlessly googling to no avail. A simple rule: if you're writing for a more general audience, always spell out your initializations the first time you say them.
- NationalPark 6y agoIn their defense, this looks like content marketing for people who buy functional programming consultancy services. So they're not really writing for a general audience. I tend to agree on initializations though. I think a lot of times people use them as a signal of their membership of the technical "in group", to establish a place in a social hierarchy. That's not good communication.
- Privacy846 6y agoThank you. The article doesn’t unpack the initialism, and the author just links to a Rust Playpen (or whatever) site which supposedly uses this three-word thing.
- chridou 6y agoEven though I see that these language capabilities remove restrictions I hope that HKTs and with them Functors, Monads, etc. will never make it into Rust. The thing I like most about Rust is that it is still a practical language where I can still solve my problems in different ways and always know what will happen. I also care about the "how" and not only about the "what". Everything being F[_]ed is what I do not need anymore.
- amelius 6y agoYou will probably not see much Haskell style functional programming in Rust simply because there is no garbage collector in Rust, and Haskell depends on one very much.
- tener 6y agoIt isn't immediately obvious that FP requires GC. Sure, the languages we have right now make use of GC, but it is far from certain that there is no way for a subset of FP features to exist in a language without GC.
- whateveracct 6y agoThe F in FP definitely hugely benefits from GC. Closures in Rust don't compare to Haskell. Combine that with GHC's optimizer and it's no contest which language to choose if both ergonomics and performance of code written largely as lambdas is your priority.
- lambda_obrien 6y agoWith linear types you could get rid of the GC, probably.
- steveklabnik 6y agoThe core issue here isn't exactly GC, though it is related. The issue is that abstraction is about hiding things, and Rust's type machinery is about exposing things. In Haskell, you have lambdas, and you have types of all of the same size data, because it's all on the heap. In Rust, each closure is a distinct type, plus the three different types of closures, plus types of different sizes... It's not so much the GC, as it is "in a GC'd language, everything is on the heap and a lot of information (from Rust's perspective) is erased." You may be able to get rid of GC in some sense, but you'd end up with something halfway to Rust, not the whole way.
- zaptheimpaler 6y agoI saw Scala taken over and destroyed by the FP fanatics who want to turn every language into Haskell. I hope they don't do the same to Rust. Just use Haskell and leave the rest of us alone! It sounds silly but its true.. as you increase the complexity of the language, the hardcore users making the libraries adopt those features, which means users have to adopt those features as well. Pretty soon everyone is talking about monad transformers and HKTs and it takes 4 hours to write something that would take 15 minutes in Python. Then the wave of FP consultancies and trainings show up so they can bill you $10000s on workshops and conferences to teach your devs monads.
- valenterry 6y agoIt's pretty flourishing though if you look at the graphs.
- zaptheimpaler 6y agoI agree its doing great for now. This new addition just feels like a minor cause for concern though. Time will tell how things play out and which faction wins - those who actually build shit or architecture astronauts making towers of monads.
- ivanbakel 6y agoYour claims are at odds with each other. If the people who are passionate about a language are the ones who pursue its advanced features (and therefore somehow force them on the rest of the world), who then is left to "actually build shit"? Why, if simple functionality is such a strong requirement of productivity, aren't those who stick to simple features productive enough to maintain an ecosystem without the Architecture Astronauts building all the infrastructure? It doesn't track that it's possible to simultaneously ruin a language by sabotaging all of its major libraries with novel features if writing code using novel features is actually incredibly difficult. It certainly doesn't track that, once you have somehow sabotaged a language's major libraries, that nobody bothers to "fix" them by introducing a new, simpler version.
- deleted 6y ago[deleted]
- zelly 6y agoToo many cooks spoil the broth I want a language designed by one very smart person, a dictator. I'm looking forward to Jai but for now the best native language is still C++. It also has too many features like Rust but at least it works.
- iamwil 6y agoWhat are GATs? I couldn't find anything in searches.
- steveklabnik 6y ago"Generic associated types," sometimes called "associated type constructors." If you want/need more than that, I can explain more, but that at least gives you the full name.
- webkike 6y agoI know some people are worried about this, but the true power of GATs is to allow you to add lifetimes to associated types. This leads to a whole avenue of new optimizations and efficient representations. It’s not about making things “more like Haskell”, that just happens to be a consequence of making more low-level operations representable in safe rust code, which if you think about it is the way rust has operated from the very beginning.