20 ms·
Very interesting insight from Graydon, in hindsight I too would have loved something more towards ML than C++. I never liked the kitchen sink approach that I se
by sph 3y ago
Very interesting insight from Graydon, in hindsight I too would have loved something more towards ML than C++. I never liked the kitchen sink approach that I see first C++, now Rust moving towards, but I respect what Rust has managed to solidify into. It's a good language.
That said, I still hate async with a passion, it makes the language more complex and not very elegant (i.e. function coloring). And now that I know how it works behind the scene (thanks to Jon Gjengset [1]), it feels so complicated and hacky, a mediocre very high level concept that someone managed to implement as a zero-cost abstraction. Impressive, but still a bad idea.
I'm sure the pro of having a BDFL instead of a committee is being able to follow a singular vision, instead of trying to appease members by adding the fad du jour which might stray a little too far from the original vision. Too many chefs in the kitchen and all.
1: https://www.youtube.com/watch?v=ThjvMReOXYM https://www.youtube.com/watch?v=ThjvMReOXYM
- TurboHaskal 3y ago> I too would have loved something more towards ML than C++ I stopped paying attention to the language around 2012-2013 or so when the direction clearly steered towards the latter. You may want to check https://austral-lang.org/ https://austral-lang.org/ out.
- dorian-graph 3y agoI was about to ask if there's any "Rust by ML" languages out there. Thank you.
- noelwelsh 3y agoO'Caml is heading in that direction. Jane Street are adding more control over memory layout and allocation, and multicore finally arrived recently.
- gautamcgoel 3y agoLol there's no apostrophe in OCaml - it's not an Irish name like, say, O'Brian.
- noelwelsh 3y agoYou're rude and you're ignorant of OCaml's history. There is no apostrophe in OCaml now, but there used to be. E.g. https://web.archive.org/web/20050207032955/http://www.ocaml.org/ https://web.archive.org/web/20050207032955/http://www.ocaml.... OCaml is / was short for Objective Caml. Hence the name used to have an apostrophe. At some point that was dropped.
- bpbp-mango 3y agonull type :(
- TylerE 3y agoNim is the closest to what I had originally hoped Rust would be. Especially Nim 2 with the new GC is really exciting. I suspect it will outperform all but the best hand tuned allocators. It's even hard real time capable, which makes it potentially viable in scenarios like games where GC traditionally hasn't been.
- pornel 3y agoFunction coloring article conflated two things: one was legitimate limitation of JS unable to wait for async result from sync call, and the other was just author's opinion how the syntax should look like. Rust's async doesn't have the limitation author described – you can spawn and block both (Tokio has some limits there, but that's Tokyo's choice, not language limitation), making "color" largely irrelevant. The second point was that both should have identical syntax, which Rust deliberately chose not to, because from Rusts perspective that would be too much implicit magic.
- sph 3y agoI disagree. You can call an async function in Rust from a regular one, that's true, but the returned value still needs to be passed through an executor to be useful. Async without function coloring means being able to call `async fn add(a, b: usize) -> usize` from anywhere and having an usize back, whether you're calling from a regular or async function. If you need slightly different logic because of async, you got function coloring. In fact, a hypothetical async without coloring functionality would not even need the `async` keyword at all, as all functions would be effectively the same, so you could choose to call one asynchronously or not with no particular ceremony. Is this even possible to do without compiler magic?
- creatonez 3y agoThing is, it's a function coloring that mostly doesn't impede you by making certain operations impossible without massive refactoring. Regardless of an extra method call needed, it gets rid of the "you can only call a red function from within another red function" barrier by allowing you to do it, as long as you specify how (execute inside this thread, or delegate to a worker thread). This makes most of the issues associated with Javascript function coloring evaporate.
- sph 3y agoFair enough. It's not a deal-breaker kind of coloring, my issue is mostly conceptual. It's simply not elegant as a construct, but perhaps it doesn't have to be.
- davedx 3y agoThat being said... python had a BDFL and look how that turned out. I think designing and evolving any living programming language is just one of the hardest problems out there. Incredible blog post indeed, was awesome to read it.
- sph 3y agoA dictator might just have terrible ideas. His dislike for functional constructs didn't do Python any favour, but now that he's gone I see Python is adding the kitchen sink as well. I haven't kept up with the language since that pattern matching proposal.
- pclmulqdq 3y agoPeople who are deeply into programming languages seem to like FP too much. That and theories of type systems. Python is doing some great things, but also just fucking around a lot.
- imtringued 3y agoAs someone who doesn't like Python. Python's problems have almost nothing to do the lack of functional programming. It does have the worst lambda syntax of any programming language but that is about it.
- alwaysbeconsing 3y ago> A dictator might just have terrible ideas. If they're all terrible, then the language won't appeal to anyone and will die off naturally. On the other hand if everyone gets to add their favorite terrible idea, there may still be enough non-terrible parts to appeal to enough people to keep momentum.
- bazoom42 3y ago> That being said... python had a BDFL and look how that turned out. One of the most popular and succesful languages, and a major force in AI innovation?
- dist1ll 3y ago> That said, I still hate async with a passion, it makes the language more complex and not very elegant (i.e. function coloring) Function coloring seems to come up a lot in these discussions, but I don't see a better way without providing a runtime. Could you propose an alternative approach to async, without sacrificing ability to write zero-overhead, high-performance bare metal systems?
- School-Cotton 3y agoThey could have simply not added async to the language. I was happily using rust before async and it was perfectly fine.
- hgomersall 3y agoSo don't use it. Why object to those that do have a use case for it?
- ninkendo 3y agoBecause it's possible the crate you want to use, uses it.
- hgomersall 3y agoOr alternatively the crate you want to use doesn't exist.
- School-Cotton 3y agoBecause I have to work with other people (both coworkers and developers of library crates).
- ay 3y agofragmentation of the library ecosystem.
- logicchains 3y agoThe Rust async story would be much nicer if they'd put in the hard work up front to support higher-kinded types, as then it could have a monadic async API like OCaml or Haskell. I don't know anyone who's used async in both Rust and Haskell who prefers the Rust approach. It'd also fix oddities like why it's possible to write a function like the following in C++ but not Rust: template<template<typename> class MyContainer> int getFirstInt(const MyContainer<int>& myContainer) { return myContainer[0]; } (In Rust it's not possible to write something like MyContainer<int>; only the int is allowed to be generic).
- chrismorgan 3y agoAre you aware of generic associated types, which landed in 1.65 (late last year)? To a considerable extent, they’re Rust’s answer to higher-kinded types. Less expressive in some ways, but fitting Rust way better, as basically a relaxation of a former restriction, rather than a new feature. If I’m reading your snippet right, GATs let you express exactly that, though minus `template`’s duck-typiness so you’d have to spell out what contract the container must adhere to.
- logicchains 3y ago>less expressive in some ways, but fitting Rust way better, as basically a relaxation of a former restriction, rather than a new feature They're not expressive enough to implement a monadic async library, as far as I'm aware, so they don't fulfill the role of proper HKTs in this context. >minus `template`’s duck-typiness so you’d have to spell out what contract the container must adhere to. Do you think it's consistent that the T in MyContainer<T> can be "duck typed" but not the MyContainer?
- estebank 3y agoIn Rust neither is duck typed. For T you must specify the traits it must have. This makes compilation fail at definition, not at the point of incorrect usage. For an example of GATs, you can see the following: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0691091fe290a60f9f13066f7299c000 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- littlestymaar 3y ago> (i.e. function coloring) I really wish people stopped using this concept, especially in the context of Rust because the `async fn`/“regular function” split is strictly equivalent to the `try_something()`/`something()` split (the first one being failible and returning a `Result` in case of failure). `Result`s and `Option`s are coloring the stack in exactly the same way a `Future` does (and `async` is pure syntactic sugar on top of future). So, someone may like exceptions and green threads more than `Result` and `async` (and this is a completely valid PoV, even though I personnaly like the explicitness better), but thinking `async` is somehow special is just a conceptual mistake. BTW, in the original blog post the “red” functions were actually functions with a callback parameter, which is actually very different.
- chmod600 3y agoThere's consistent use of Result and Option in std, community agreement to do things that way, and it's pretty easy to use them. Tons of stuff in std and many other crates don't use async. A lot of stuff is harder and more code to do async. A lot of it is much harder to read. That's the difference.
- littlestymaar 3y ago> There's consistent use of Result and Option in std, Well, there are dozens[0] of incompatible error types in std, two of them being just called `Error`[1][2]. Everytime you create a function that use more one of the different error types from std (for instance, an I/O error and a TryFrom-related error) you have to create a custom Error enum to accommodate the different kinds of errors from std… > community agreement to do things that way And the community agreement is e̶r̶r̶o̶r̶-̶c̶h̶a̶i̶n̶, ̶f̶a̶i̶l̶u̶r̶e̶, ̶ s̶n̶a̶f̶u̶, this_error, unless it's for a binary crate and then it's ̶a̶n̶y̶h̶o̶w̶, ̶e̶y̶r̶e̶, color-eyre. The error ecosystem is much more fragmented than the async one (even though I think this_error is here to last, so I don't have to migrate once again…). In the async world, there's pretty much tokio (but I think it would actually be better if it interoperated better with other runtimes so alternatives could emerge) > A lot of it is much harder to read. Ah yes, fut.await is fundamentally harder to read than res?, right? Overall, you vastly underestimate the friction that the `Result`-based error handling adds. I still think it's worth it, and it's been slowly getting better (did I say I loved this_error?), but it remained one of the biggest point of friction in Rust over the 8 years (time flies) I've been using it. Async is comparatively less disrupting (mostly because it only appears in the call stacks where you're doing I/O, whereas errors are ubiquitous). [0]: https://doc.rust-lang.org/std/error/trait.Error.html?search=Error https://doc.rust-lang.org/std/error/trait.Error.html?search=... [1]: https://doc.rust-lang.org/std/io/struct.Error.html https://doc.rust-lang.org/std/io/struct.Error.html [2]: https://doc.rust-lang.org/std/fmt/struct.Error.html https://doc.rust-lang.org/std/fmt/struct.Error.html
- osigurdson 3y agoI think a big part of any successful endeavour is alignment of authority, responsibility and competence. The problem with group decision making is responsibility is very diluted. If a bad decision is made, no one in particular is responsible.
- fzeindl 3y agoI hate async as well. Developers should have learned about communicating sequential processes and blocking queues and none of that would have been necessary. It creates a weird divide in every language. Just learn about threading and do it. A nice talk about this: https://www.reddit.com/r/programming/comments/da141r/ron_pressler_pull_push_please_stop_polluting_our/ https://www.reddit.com/r/programming/comments/da141r/ron_pre...
- duped 3y agoDevelopers should learn the difference between concurrency and parallelism because async isn't equivalent to threading.
- Freedom2 3y agoYep, it's pretty alarming that a developer would toss away async when threading isn't a fit for all situations - what happened to the right person tool for the job? As a professional, I 'hate' some things too, but I'm not going to blindly make my work harder and less efficient out of some small preference I have.
- tomjakubowski 3y agoasync has fragmented the crates ecosystem. If you want to want to write async-free code with threads, you'll probably still have to opt-in to an async executor for some dependency because these days many libraries will implement only an async interface
- duped 3y agoI think you're manifesting what I'm talking about. The reason you cannot replace async with threads has nothing to do with the ecosystem, but the simple fact that threading is a model of parallelism and async/await is a model of concurrency. These are two subtly different concepts in software architecture, but they are not the same thing. More concretely, threaded code has nothing to say about whether or not its synchronous or asynchronous, and asynchronous code has nothing to say about whether or not it's evaluated in parallel.
- divan 3y agoIt's crazy that the programming community even accepted the concept of async/await as a sane one. Being sync or async is essentially a property of the attention of the caller, not of the action itself. Is "eating a donut" a sync or async action? If I'm focusing all my attention on it, essentially putting all tasks aside (after) - then it's a synchronous action. If I'm reading a book/watching a video/walking/etc, while eating – it's an async action. How does "func EatADonut() async {}" aka "eating a donut is an inherently async action" even make sense to people?
- paulddraper 3y agoIDK about donuts. But consider a network request. The vast majority of the time is not spent in the CPU. Right?
- divan 3y agoDoesn't matter, donut was just an abstraction. s/eat a donut/make a network request/ Is "network request" a synchronous or asynchronous activity? It depends whether your code blocks and wait's until response (or timeout) or continues executing and handling it when it comes. It's property of the "attention" of the caller, not of activity itself.
- joshuamorton 3y agoBut "can be gracefully executed asynchronously" is a property of an action. Something that pegs the CPU to 100% because it's doing intense processing isn't a good candidate for async. Similarly, some code can cause issues when written in a non-streaming fashion. Take the following example (in python): x = [x for x in range(1_000_000_000)] y = (x for x in range(1_000_000_000)) If you spawn a bunch of async workers doing the first one concurrently, you'll OOM your system. If you spawn a bunch of async workers doing the second one concurrently, your system will be fine. In other words, "async" is a label on a box of donuts that implies (though doesn't ensure, people can of course still do bad things) that the donut won't explode if you look away from it.
- mananaysiempre 3y ago> [Async/await] feels so complicated and hacky, a mediocre very high level concept that someone managed to implement as a zero-cost abstraction. I feel like I need to point out that Donald “Structured programming with GO TO statements” Knuth included an example of coroutines in the first volume of The art of computer programming, the first edition, dated 1968. In assembly language for an accumulator machine. With a box of scraps!^W^W^W^W^WThat is to say, that C and most other languages have made coroutines awkward and thus virtually unused in the past three decades or so does not mean they are particularly novel or high-level. Granted, Knuth used coroutines in a simulation and not for I/O, so he did not need that much of a scheduler, but still.
- throwawaymaths 3y ago> a zero-cost abstraction It's just an abstraction, it's not zero-(runtime)-cost. It might be the "lowest possible cost", still nonzero.
- mplanchard 3y agoA zero cost abstraction is just an abstraction you couldn’t write any better yourself, not one that is actually free. If you’re lookint for an item in a hashmap, you still have to pay the cost of looking up the key. The promise of zero-cost abstractions is that you’re paying the lowest possible cost to do that, with a nice interface that you didn’t have to write from scratch.
- throwawaymaths 3y agoAgreed, that's why I said might. Whether rust async is the best possible abstraction probably depends on the event loop, and your code. I could be wrong but I think if you write a trivial async function it will still have spinach for the async even if there are no yield points and it's effectively a non async function
- jaxrtech 3y agoIf Rust was a higher-level language, then I'd say yes, just automatically handle running Futures in parallel, join them when you actually need to resolve the data, and pretend that they look like synchronous functions in the 80% of cases. Though things like `select!` wouldn't make any sense mixing the two. I continue to find the "function coloring" argument misses the point unless you're arguing from a developer experience perspective. Why should two fundamentally different things look and function the same? Want this the ultimate pitfall of early RPC implementations where everything looks synchronous? In Rust, a lot of the friction is due to how async functions/futures fundamentally differ in model of execution and how that interplays with the rest of the language. Other languages get to hand-wave a lot of these problems away with a GC. (It certainly could be less of a pain nonetheless.) - Futures don't execute until polled, can partially execute if something isn't ready (and would block), can partially execute and be arbitrary cancelled and dropped. There is no equivalent in sync functions unless you make them co-routines. - Since you can "pause" futures if something would block, you need a place to store the function's state for when it's resumed. Now you must be consider if the async function's state is `Send` if the future wants to move to a different thread to continue executing -- which is why you see `pin!` used. Sync functions don't care about this since you always run to completion on the same thread, and the stack is ephemeral. - Likewise, the `Future` returned by the async function is going to need to encapsulate it's state that it saves. In the general case, this is a compiler generated anonymous struct that changes if any state saved across `.await` changes, hence the opaque `impl Future`. This is why you see `BoxedFuture` a lot to abstract this away at the expense of an allocation. Ideally, the new associated types with lifetimes can avoid this with traits. So if all functions were co-routines (i.e. functions that can be resumed and re-entered) they would all have the same "color". But all you really did was "lift" all sync functions to be "async" functions with no internal await points. (IMHO, if the C# team back in the day decided to implement full blown co-routines into the language instead of just `async/await` as a compiler trick, I think many other projects would have followed suit with a more general co-routine solution instead of treating `async/await` as this special thing which is just a specific compiler generated implementation of co-routines.)
- bsder 3y ago> That said, I still hate async with a passion, it makes the language more complex and not very elegant (i.e. function coloring). async in Rust is a popularity issue even more than a technical issue. A whopping amount of people who are coming to Rust are doing so because they want a good ecosystem for implementing network service servers. Rust/cargo/crates hits the sweet spot for them. I am with you that I loathe async/await in Rust. However, I also have to acknowledge that without async/await, Rust is a vastly less popular language. All the other uses of systems programming are simply dwarfed by number of people building network services. That's just the sad reality.