-
Notifications
You must be signed in to change notification settings - Fork 153
Update Client.send to return Task instead of result
#74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
eb9a270 to
c81abff
Compare
|
@mattmassicotte As someone well-versed in Swift concurrency primitives and API design, do you think this is a reasonable approach? Any suggestions to making this more flexible / ergonomic / conventional for API consumers? |
|
Timeout is a very common problem with concurrency in general, and one that isn't well-supported by built in stuff today. But in this case, couldn't a consumer just wrap the original call in a |
|
@mattmassicotte Thanks for weighing in!
Yes, that's right. I don't have a good mental model for the overhead introduced by wrapping a task in another tasks; conceptually it feels wasteful, but maybe that's an emotional response. Taking another look not and checking my priors, I think a lot of this was motivated by a thought I had that returning a So the only possible argument I can make for this approach is that returning a
Fair point! Can you point to any projects that you think do a really good job at API design for a client like this? Something that supports cancellation and retries. |
|
I think the intention is that structured concurrency ( Reties is an interesting question. My gut is that such a concept has to be internalized somehow. Ultimately you have to either return from an async function or throw. Timeout support is a well-known and frequently encountered gap in the current system. Modelling it with a group, like you did, is a very common approach. But I find it quite a bit of boilerplate and a little on the error-prone side. Unfortunately, I have not encountered any great specific examples that I can point to. But, here are few that I have noted. I've never used any, but perhaps there's good stuff in there. https://github.com/ph1ps/swift-concurrency-deadline |
That's what I really needed to hear. I think coming most recently from Go, Python, and Rust, my mental model of concurrency has involved explicit cancellation. So I just need to reset my expectations for structured concurrency.
Thanks for confirming that. Good to know I wasn't missing something obvious. At the risk of getting in the business of providing low-level concurrency primitives, I wonder if it'd be reasonable for this SDK to provide some basic helper methods for the most common use cases. I quite like the implementations you linked by @ph1ps. If I do add helpers, my plan would be to inline those with attribution as internal functions, and then export public static methods on |
I think this really comes down to how common a problem this will be for clients. If a first-party solution ever materializes, it would undoubtedly cause churn. Finding a balance here is tricky... |
|
Alright, thanks to the insights from @mattmassicotte in this discussion, I believe there are better ways to address the problems we identified than what this PR does, so I'm closing this for now and opened #93 to track the implementation of an alternative approach. |
Resolves #35
Alternative to #72
The current
send()method returnsasync throws -> M.Resultwhich means the request is already executing when the method is called. This makes it impossible to:This PR changes
send()to returnTask<M.Result, Swift.Error>instead of usingasync throws. This gives callers more control over request lifecycle:This would be a breaking change to the public API - callers would do
try await client.send(request).value(emphasis added) to get the same behavior as before.I think this makes sense for the
send()method, as it's lower-level than the request specific methods, likeping()(which itself callssend()). What I'm still wondering is, should those higher-level APIs should also be updated to returnTask? Or is it reasonable to ask API consumers to use the lower-level API if they want more control?