Notes on structured concurrency, or: Go statement considered harmful
171–180 of 234 posts
Re: Notes on structured concurrency, or: Go statement considered harmful
#172Earlier quoted context omitted.
I don't think that's a misunderstanding at all. The argument is all about control flow and programmer understanding. The actual underlying mechanisms aren't what he is arguing against, instead he dislikes the potential for programmer confusion when code is being executed that wasn't expected. That is 100% possible with goroutines. Method calls aren't really branching. The code path is still totally linear. You could…
> That is 100% possible with goroutines. It really isn't. The big difference is that "goto" can lead to code you read being grossly misunderstood due to complicated flows, potentially even absurd things like jumping to a different function. (And as you mention, good code can be written with goto's—it can make particularly error handlers much easier to read in C.) This is not the case at all with "go", which is extrem…
Could you say more about why? It seems to me that purely synchronous code is much easier to reason about than async code, in that a) less is happening at once, and b) things happen linearly. That seems less complex almost by definition.
I'd certainly believe that things like goroutines are better than the threading that came before, with way less cognitive load. But I can't see how it's less complex than linear code.
Re: Notes on structured concurrency, or: Go statement considered harmful
#173I don't like the term "nursery" (maybe "highway" or "complex" or... something else) but this seems to be a good design change, unless I'm missing something
Re: Notes on structured concurrency, or: Go statement considered harmful
#174I thought the title was kinda clickbaity, but it turned out to be a great article. Also the comparison to goto really effectively conveyed the point he was trying to make. I have two questions though: * Does anything else like this currently exist (other than the Trio library he mentions), which shows that it's a superior paradigm in practice? * What are the cons to this approach? Why not do it?
> * Does anything else like this currently exist (other than the Trio library he mentions), which shows that it's a superior paradigm in practice? Erlang's supervisor behaviours are basically that. They add some more stuff (the actual supervision) but fundamentally every process in a supervision tree will necessarily manage and outlive all its children processes.
Re: Notes on structured concurrency, or: Go statement considered harmful
#175What. This article proposes a "nursery", which is just a wrapped sync.WaitGroup/pthread_join/futures::future::join_all/a reactor that waits for all tasks to terminate/etc. It then uses an exception-like model for error propagation to "solve" error handling (which is fairly easy to handle with a channel). The construct is a decently usable, already applied tool to handle a set of problems, but the article takes the is…
Agree with everything. I think the misunderstanding here is that goroutines are not classical threads (which would share many problems with `goto`) - they exist somewhere between coroutines and actors (because of channels+select). Actors are an established and mature solution to many headaches surrounding concurrency and parallelism. Goroutines share more in common with method calls (which are a form of branching) an…
I don't think that comparison makes sense. Channels and select are not a property of goroutines themselves, nor is using those the only way to communicate with other goroutines. The actual functionality of a goroutine is very similar to a thread, even if the broader language/conventions push it towards a coroutine/actor.
> Goroutines share more in common with method calls (which are a form of branching) and even with single-threaded scenarios, methods can do surprising things - especially if you have shared global state.
But they can't run in parallel, or data race. Goroutines, like classical threads, can have data races.
> Additionally, if you control how data is shared (such as message passing - channels) you shouldn't have to be concerned about what the other thread is doing - so long as it reacts to messages that you send to it.
This is also true of threads. Message passing is a layer on top of, and agnostic to, some concurrency mechanism. Go might make it slightly syntactically simpler than languages that use true OS threads (much simpler than C's pthread_spawn, but not particularly different to Rust's thread::spawn(some_closure)), but that's a syntax layer.
Re: Notes on structured concurrency, or: Go statement considered harmful
#176Earlier quoted context omitted.
But this concept does also solve that problem. Maybe more simply. Rust is renowned for being a total headache with borrowing and lifetimes. Nurseries might be a simpler solution for this. As you say, a corner case. But a common one...
Nurseries and lifetimes are orthogonal concepts. In fact, Rust already has a "nursery", except it's generally called a scoped threadpool. They use lifetimes to ensure the threads can reference stuff on the stack without copying it. So a "nursery" is basically just a scoped threadpool with a 'static lifetime.
Re: Notes on structured concurrency, or: Go statement considered harmful
#177Earlier quoted context omitted.
You realise that makes you sound exactly like an old Fortran programmer refusing to give up goto? A for loop is just a wrapped goto! ;-) I think the author proposes a useful way to structure multi-threading. The comparison to goto isn't perfect and he needs to play fast and loose with some terms to keep the analogy working but he makes a good point. I'm not convinced yet that the nursery pattern should be the only al…
As someone whose work includes hardware driver programming, I'd rather not give up goto. It would make a lot of error handling extremely cumbersome, and much less readable. :( However, I find "go" and "goto" to not intersect at all. I've written this many times in other comments on this thread, so I'd rather not type it out again, but the TL;DR: is that "goto" can make understanding a function when read difficult, wh…
I believe our industry’s several decades of demonstrated inability to write concurrent code correctly disagrees with you.
Languages like go (and Rust, to a greater degree) have improved the situation. But there are still a ton of sharp edges that existing approaches still have, and—often—they’re ones that aren’t readily apparent until after a project starts growing and begins unearthing issues that are rare, only happen on faster hardware, or require higher levels of concurrency than we’re previously used.
Re: Notes on structured concurrency, or: Go statement considered harmful
#178What. This article proposes a "nursery", which is just a wrapped sync.WaitGroup/pthread_join/futures::future::join_all/a reactor that waits for all tasks to terminate/etc. It then uses an exception-like model for error propagation to "solve" error handling (which is fairly easy to handle with a channel). The construct is a decently usable, already applied tool to handle a set of problems, but the article takes the is…
Most articles riding on the "x considered harmful" meme are clickbait without much content.
Re: Notes on structured concurrency, or: Go statement considered harmful
#179Earlier quoted context omitted.
I don't think its a superior paradigm, just a different one. I only see his nursery as being useful when you really want your async tasks to complete before the function in which they were dispatched returns. That's far from covering every use case of concurrency! A lot of the value of concurrency is in background operations. These simply can't be tied to the duration of a function call on the dispatch thread. Doing…
> I only see his nursery as being useful when you really want your async tasks to complete before the function in which they were dispatched returns. That's far from covering every use case of concurrency! Perhaps, but only having a complex solution that handles 100% of use cases is less desirable than having a simple one that handles 80% of the most common use cases PLUS the ability to go deeper and use the complex…
Still, there's value in unification and generality, at least when the result is simple, even if that covers only 80% of cases.
For example you generally run a lot more composable tasks than actual threads and syncs. You get a general, and powerful abstraction for the general case yet threads are still there for more exotic needs.
Re: Notes on structured concurrency, or: Go statement considered harmful
#180Earlier quoted context omitted.
> I only see his nursery as being useful when you really want your async tasks to complete before the function in which they were dispatched returns. That's far from covering every use case of concurrency! His section "There is an escape" answers this criticism: "The nursery object also gives us an escape hatch. What if you really do need to write a function that spawns a background task, where the background task ou…
Then what is the difference between the nursery escape and joining a thread or extracting the value of a promise? Both of these will bubble any error back into the dispatch thread. Seems like we're just layering indirections (I wouldn't call that an abstraction) for little added value over the existing constructs.
TL;DR, this doesn’t add expressive power (in fact, it removes it). The author argues that the power removed is power that is unnecessary, dangerous, and inhibits the ability to infer higher-level invariants about your programs that can improve understandability while potentially enabling even better abstractions. Even more briefly: restrictions make your code better.