6 ms·
And yet we somehow don't acknowledge the fact that this is just do-notation for some specific monad--yeah, that powerful abstraction that can't be expressed in
by lapinot 8y ago
And yet we somehow don't acknowledge the fact that this is just do-notation for some specific monad--yeah, that powerful abstraction that can't be expressed in Rust because we don't allow higher-order polymorphism. Don't get me wrong, i'm bitter because i feel like Rust really is almost in the right direction for the future of language design. Yet there is a long time before we get a language with a really precise type system (linear, "pure") and thus with highly efficient code generator (~rust), that at the same time has all the higher abstraction goodies, maybe even dependent types (cf quantitative type theory [1]). Of course they took that into account, it's even explicitly mentioned in the rfc, and it got turned down for being too experimental, but at some point you gotta make a bold step forward and force people to actually care about abstractions.
Now that Rust is getting some maturity i worry that it gets too complex in too much direction just because it's not powerful enough to express the few common abstraction, of which several instances are being added as distinct concepts (async&try, const datakind, higher-order poly--especially for lifetimes, named impls, before-after memory state for references...).
[1] https://bentnib.org/quantitative-type-theory.pdf https://bentnib.org/quantitative-type-theory.pdf
- steveklabnik 8y agoSupporting do notation or not isn’t some sort of position we’re taking as a language; it’s an open research problem if do notation can work in Rust. Until that’s solved at all, we’re just not sure it’s possible. (Previous version of this comment said "HKT" but that's not purely right; we have a path forward for something at least HKT-like, but do notation is the bigger question.)
- lapinot 8y agoYeah, sure, i understand that, hn comments just need to be a bit spicy (btw, any ref of papers or other stuff in that area? i didn't follow closely but didn't see too much moving, actually i don't even know what the big question marks are). Although i really believe it's not about if but how, so let's hope that when the time comes Rust will be able to take the tough changes necessary to unify the different branches that split off. Like what is the real road-blocker, since we're talking here about abstractions that are gonna be monomorphised (at most into lifetime-polymorphic function, which anyway get erased) and known statically, right? Is it only a problem of getting the typing rules consistent and robust?
- steveklabnik 8y agoThere's multiple issues. The biggest ones that I can recall, off the top of my head: First off, do notation desugars to closures. But Rust has three different kinds of closures. How does that work? Furthermore, Rust has imperative control structures. How does all of this interact with each other? Are you now no longer allowed to use those constructs inside of do notation? That feels inconsistent. I don't know of any languages that have something like do notation but don't have everything boxed. It makes everything easier, but that's not generally acceptable in Rust. For example, we can use async/await with no dynamic allocation. Could we with do notation? "Open question" doesn't mean "impossible", mind you. But nobody has ever come up with a design. In the meantime, we have users to support...
- lapinot 8y agoThanks, that's interesting, i should dive a bit more into how async is implemented. On "which closure kind to choose" i feel like that's not the worst part (closure syntax already infers which trait to implement), but the fact that it is a closure and how it fares with allocation seems not trivial.
- kibwen 8y agoFor the moment, you can see the progress on empowering the type system in the RFC for generic associated types, which is intended both to be the simplest extension to the type system that allows for some important patterns (e.g. streaming iterators and collection traits) and to be forward-compatible with any additional extensions in the direction of HKT. Importantly, it's also intended to be intuitive for users who have no prior experience with higher-kindedness and usable without (hopefully) requiring anyone to learn anything about type theory. The RFC text can be read at https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md https://github.com/rust-lang/rfcs/blob/master/text/1598-gene... and comments can be left on the tracking issue at https://github.com/rust-lang/rust/issues/44265 https://github.com/rust-lang/rust/issues/44265 .
- steveklabnik 8y ago
- withoutboats 8y agoI don't have time to write a whole essay, so let me just establish my credentials: - I wrote the generic associated types RFC (how Rust will implement higher kinded polymorphism). - I wrote the const generics RFC (the closest Rust will get to dependent types). - I wrote the async/await RFC, as well as the linked blog post. That is to say that I am intimately familiar with how Rust's type system can be extended to support more "powerful" abstractions. Monads as implemented in pure functional programming languages like Haskell cannot usefully abstract over asynchronous and synchronous IO in Rust for a variety of reasons having to do with the way the type system exposes low level details by virtue of Rust being a systems programming language. I do not believe that `do` notation could be a useful mechanism for achieving either the ergonomics or the performance that async/await syntax will have in Rust. I'm responding to you because you're the top comment, but I could write a similar response to a lot of comments here. Monads, stackful coroutines, green threads, CSP, etc - we've heard of them! :) We have well-motivated reasons to choose async/await: its the only solution that meets our requirements.
- nazka 8y agoOut of topic. Wow withoutboats you didn't have an account here before? Welcome! That's great to see you coming here and to see you giving many in-depth and very precise answers.
- canndrew2016 8y ago> Monads as implemented in pure functional programming languages like Haskell cannot usefully abstract over asynchronous and synchronous IO in Rust for a variety of reasons having to do with the way the type system exposes low level details by virtue of Rust being a systems programming language. I do not believe that `do` notation could be a useful mechanism for achieving either the ergonomics or the performance that async/await syntax will have in Rust. Sorry, but I don't buy it. I had a half-baked, unfinished proposal for an effects system that would have allowed Rust to implement async/await just as efficiently (no stackful coroutines) along with any number of other effects [0]. Maybe it wouldn't have been a good idea due to stretching Rust's complexity budget too far, but that's very different from saying it's impossible. Having watched the development of Rust closely I really think that the design team just didn't understand the theory side well enough to be able explore the design space here. (I'm not being as critical as I might sound, PL theory is hard and the Rust devs have wielded it much more competently than the designers of any other non-research language). [0] https://internals.rust-lang.org/t/start-of-an-effects-system-rfc-for-async-etc-is-there-any-interest-in-this/7215 https://internals.rust-lang.org/t/start-of-an-effects-system...
- kuon 8y agoI have been doing programming, including functional programming for more than two decades, I still don't really know what a "monad" is. Each time someone explains it to me, I understand something different.
- Matthias247 8y agoThe way I accepted it after years of confusion, and without still understand the word: A monad is any type which supports 2 specific operations. One maps an type into it's monadic form, and the other allows to apply functions to monadic types and returns the monadic type again. The latter is sometimes called bind or flatmap. For Future types, the first thing could be called MakeReadyFuture(type), the second one Future.then(result => functionWhichReturnsANewFuture).
- pimeys 8y ago... which allows a generic composition enabling imperative programming in a purely functional language. Haskell has do notation, that sugars the syntax to look like one is writing imperative code, even though it's all about composing Monads.
- pas 8y agoMonads are containers for which .map, and .flatMap (>>= in Haskell) make sense. Future, Option, List, State/IO, etc. And the do-notation that some comments mention become a long list of flatMap/filter/map invocations. do { futRecord <- getDBrecord(username, passw) uid = futRecord.map(record -> record.id) username = futRecord.map(record -> record.name) futResult <- setUserLastLoggedIn(uid, now()) futResult <- checkUserACL(uid, operation.roles) } yield username This is just an abstraction, and it's not exactly a pretty one, but better than the flatMap hell in functional languages. The do-notation uses the same monad throughout, in this example the Future one. The final yield is a map, the "<-" lines all represent a flatMap. Finally, Monads are very much like Vector Spaces in Maths. You have Axioms for both, and if the object satisfies them, it's a Monad or Vector Space! https://devth.com/2015/monad-laws-in-scala https://devth.com/2015/monad-laws-in-scala https://en.wikipedia.org/wiki/Vector_space#Definition https://en.wikipedia.org/wiki/Vector_space#Definition
- deleted 8y ago[deleted]