3 ms·
Read the article but honestly don’t fully understand it. How does using this not end up with one big nursery started somewhere in your main passed down everywhe
by sfvisser 4y ago
Read the article but honestly don’t fully understand it. How does using this not end up with one big nursery started somewhere in your main passed down everywhere and basically scoped to the entire app lifetime? Getting you in back in the same spot as before.
My (rust) code using Tokio starts a bunch of tasks in main that live for the entire lifetime of the app. They’re independent and communicate over channels with each other – and possibly the outside world.
Hard to see what problems this causes that Trio can fix. But maybe I’m zooming in on the wrong use case?
- MarkMarine 4y agoKind of a contrived example but let’s say you’re going to kick off a job that downloads a “Post” and associated data to display it, like a “Profile” and “Comments” (with sub comments, profiles, etc) but you decide that hitting an unhandled error in the Profile loading stage should cancel loading all the rest of the sub-jobs you’ve kicked off. Nurseries do this for you, you scoped the whole chunk of concurrent operations and if an unhandled exception bubbles up to the nursery supervisor, you can cancel all the subsequent parts of it. Then you can handle that issue if you choose to, or just ignore loading that post. If you scope that to the whole program, you’d have to handle that failure in the whole program. If you scope these things more finely, you can just cancel and retry or ignore that chunk, without bleeding that logic down into the stack and increasing complexity.
- sfvisser 4y agoI can see how this works, but my personal experience is that concurrent code that is finely scoped is easier to reason about than larger-scoped concurrency already anyways. Hardly a controversial statement, I guess. So if nurseries help me reasoning about finely scoped stuff even better that's great of course, but only solves part of 'the problem'. And maybe not the most interesting part. Just quickly running a task to perform a handful of stuff concurrently for a single purpose (like doing a few network requests and packing the results) is hardly where I encounter big issues. The compositional behavior of Futures really helps here I think. A bunch of `Future<Result<A, Error>>` go a long way.
- MarkMarine 4y agoI’ve liked using Kotlin’s coroutines which put in place the behavior described in the article, and I like them in comparison to unscoped concurrency that exists in Go and other languages by default, or the addition of a context that needs to be passed to every function, because I don’t like it polluting the type signature of the functions. I actually have found rusts futures/results to be more cludgy than scala, were I could flatmap in a fairly nice syntax provided I was fine with using transforms. Do you pass an early cancellation variable to your future returning rust functions?
- skybrian 4y agoI think the idea is that you can do that for tasks that are meant to run forever. It's global scope. But you use smaller scopes for tasks that are supposed to shut down. In Go terms, it's like having an automatic waitgroup so you can't leak goroutines. It doesn't sound like you need it for what you're doing.