Live data from Hacker News

The Rust I wanted had no future

graydon2.dreamwidth.org

371–380 of 523 posts

Re: The Rust I wanted had no future

#371

I love Rust and built some production code with it in the past. But nowadays I want something more simple so that not-so-senior developers can pick it up quickly, and I want flawless tooling, and willing to sacrifice a bit of performance. So basically I often end up with Go. Go is exceptionally great in tooling, ecosystem, any objective metrics like build times or crosscompilation... but I still don't like the langua…

>like Rust has (Option instead of `err != nil`

Didn't they implement generics already?

https://go.dev/blog/why-generics

It seems like their implementation has enough power to do this in most cases, although "Option" is not built in.

Re: The Rust I wanted had no future

#372
post #361
post #261

Earlier quoted context omitted.

It'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, whil…

How about waiting until all or part of donut pack is eaten by multiple eaters? Jokes aside, async/await comes from easier handling of a callback code and automatic function splitting. Writing asynchronous donut eating in loops or with yielding of partial results is very easy to swallow with such syntactic sugar. The whole model is very easy to grasp and to work with. I'm not saying that's the best solution but it def…

Doesn't matter. You either wait until donut is eaten by others or you don't. Caller is deciding if it's "synchronous" (blocking) action or not.

The question is how you translate mental models from real world to the code, and async/await fails here spectacularly. It's just weirdly unnatural thinking about sequencial processes. It requires tremendous amount of cognitive gymnastics just to reason about simple things.

Re: The Rust I wanted had no future

#373
post #6

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 kno…

I 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_pre...

I'm pretty sure the designers of Rust knows about CSP. Rust has Channels in the standard library after all: https://doc.rust-lang.org/rust-by-example/std_misc/channels....

Rust Channel have the nice additional that they don't panic, and don't have all the weird Go Channel Axioms[1] that you have to wrap your head around to get things to work correctly[2].

For what it's worth, I don't think Java Virtual Threads (a.k.a. project loom) has implemented an explicit concept of channels, although I guess BlockingQueue could work as a channel.

There is a big issue with bringing CSP to languages like Java 21 or Go, that use so-called "colorless concurrency" or "stackful coroutines". CSP losses a lot of its modeling power when you combin shared mutable state and pre-emptive (or invisible) parallelism. And this is exactly what both of these languages do.

"Colored" abstractions like async/await are more cumbersome (especially in Rust), but they let you know exactly at which points of execution your stackless coroutine may suspend, and you can handle synchronization accordingly.

Those functional concepts are cumbersome, but threads ("green", "kernel", "virtual", "goroutines" or otherwise) are not the panacea their proponents claim them to be. I had to debug way too many data races in my life to consider them perfect. Rust still has a lot to improve on its async front (no async traits, async closures or async iterators and the async runtime confusion), it can at least promise me that I won't get any data races.

[1] https://dave.cheney.net/2014/03/19/channel-axioms [2] https://www.jtolio.com/2016/03/go-channels-are-bad-and-you-s...

Re: The Rust I wanted had no future

#375
>a viable c++ alternative

Wow, so one of the principal creators understands that that's what it is, not a replacement for everything under the sun.

And it's a GREAT c++ alternative, but ffs please stop telling me to use it for everything from webdev to embedded.

Re: The Rust I wanted had no future

#376
post #2

A bunch of things you don't like about Rust? Turns out that the person who originally created the language doesn't like them either. I know that the main point is about governance and how having a BDFL would have led to a completely different language but I really would have preferred the Graydon-BDFL-Rust to what we have today. Very interesting article, worth a read.

I think I mostly want a language like Graydon-Rust, but not with the Expressivity tradeoff (or at least not as hardcore as it sounds like Graydon is).

Re: The Rust I wanted had no future

#377
post #261
post #6

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 kno…

It'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, whil…

> How does "func EatADonut() async {}" aka "eating a donut is an inherently async action" even make sense to people?

Of course it does, read it as "beware, something blocking down the road".

If you can EatADonout without blocking, please do, but want it or not that's a different implementation, one that doesn't block and the signature it's telling you so.

We're so used to sync and having hidden blocking operations. I wonder if in an alternate universe the first languages considered the blocking/async nature of operations and then some newer languages considered hiding this information into seemingly sync functions would produce similar but opposite outrage against it.

Re: The Rust I wanted had no future

#378
post #261
post #6

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 kno…

It'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, whil…

>How does "func EatADonut() async {}" aka "eating a donut is an inherently async action" even make sense to people?

You have to think of it from a higher level.

Nobody knows if eatdonut is strictly async but everyone knows all IO calls are async. So something like socket.send would be async, this is obvious.

Then anything that calls socket.send would then in turn become async itself. The async sort of spreads around like a virus. Any function that uses other async functions gets polluted with the async marker.

So in this sense, you should think of it like this. If eatADonut is async it means it touches IO. It sends data somewhere else to some other thing. It also means it's not unit testable.

In a nut shell This is the intuition you should have about async:

   Async Tells You about the properties of eatADonut. eatADonut doesn't tell you anything about whether or not it's async.
This is largely identical to how the IO monad works in Haskell. Unless you want all your logic polluted with the IO monadic wrapper or the async marker you have to learn to segregate your functions into async functions and non-async functions.

Under this intuition, EatADonut, by good design principles should NOT be async. This is what it should be:

    fn eatAdonut() -> ResultOfEatADonut
    async fn sendDataToIO(data: Data) -> ()

    async parentCaller() -> () {
       let data = eatAdonut();
       sendDataToIO(data.serialize()).await;
       ()
    }
The async marker forces you to organize your code in this way, You have to do this unless you want all your functions to be polluted by the async marker. This is the proper way to organize code Anyway, as it allows your code to be unit testable. Async calls are, in general, not unit testable because the functions are not pure. So if you look at what I did above, I segregated eatAdonut Away from IO and made part of the logic of the overall code testable via unit tests.

IO calls should be very general and small functions while your other functions should be pure and unit testable. This is the overall intuition you should have about async.

Believe it or not, this specific aspect of async is actually an improvement over golang style concurrency. Not an overall improvement, it's still debatable which style is better, but I'm saying the async marker is actually a feature that other styles of implementing concurrency don't offer.

Basically, async encodes the concept of IO and function purity into the type system. It allows the type checker and You to tell which functions touch IO and which functions are pure and unit testable.

People think unit testability is some kind of marginal benefit because you can cover testing with integration tests which are basically tests that happen across IO. It's more than that. The ultimate dream of programming something as if it's a bunch of modular lego blocks that you can elegantly place together is most fully realized with pure functions. Async enables more of this style of modularity in Rust (but not without some cost which is why it's debatable whether or not it's better than go style concurrency).

Re: The Rust I wanted had no future

#379

Earlier quoted context omitted.

C# is horribly OOP. I use it in my day job, and the frameworks wield OOP overcomplexity proudly. It has likely gone beyond Java as the posterchild for OOP: interfaces that are implemented once, classes that are instantiated once. You can't get away from it either, it is practically part of the stdlib and everyone cargocults it. Rust is imperative too, though.

The language doesn't push you towards interfaces implemented once, but many developers indeed persist doing it for no reason at all. With proper code review we're able to make that practice go away on the projects I'm working on.

Lots (all?) major frameworks push for dependency injection though, where interfaces are a must, as far as I know (not for DI-the-principle, but DI-as-implemented). ASP.Net Core is a good example. It's not C# at the language level forcing interface-driven development, but frameworks like ASP.Net are so tightly integrated that boundaries blur.

Re: The Rust I wanted had no future

#380

I love Rust and built some production code with it in the past. But nowadays I want something more simple so that not-so-senior developers can pick it up quickly, and I want flawless tooling, and willing to sacrifice a bit of performance. So basically I often end up with Go. Go is exceptionally great in tooling, ecosystem, any objective metrics like build times or crosscompilation... but I still don't like the langua…

Perhaps you'd be interested in Inko ( https://inko-lang.org/ ). It's obviously not there yet in terms of tooling and what not, but it might scratch an itch for those looking for something a bit like Rust, but easier to use. Disclaimer: I'm the author of said language :)

Lots of languages are aiming for "like Rust, but easier to use" -- I think I could name half a dozen. It's a laudable goal!

I'm curious how references in your language work. I see the very small example, but it doesn't explain much.

Some questions in that regard: Is `&T` a type? Can you store it in a structure, or return it? Can you have a reference to a reference? If you can have a function `f(&T, &T) -> &T`, how do you distinguish whether the reference it returns lives as long as the first or second argument? If references can't be stored in structs, how do you do non-owned iterators, or string slices?

Post reply on HN