The issue with bringing this up now is that much of the core team is distracted by the work of finalizing the Swift 3 implementation, and won’t be able to fully participate.
I have a ton of questions about how opinionated the language will be with regards to concurrency approaches (I’m in the camp that believes Erlang, Go and Node.js have shown opinionated approaches are needed for widespread adoption of concurrency within a language). I’m waiting patiently :-) -DW > On Jul 14, 2016, at 4:57 PM, Charles Srstka via swift-evolution > <[email protected]> wrote: > > Is there a way I can make that clearer than putting it in a section labeled > “FUTURE DIRECTIONS”? > > Charles > >> On Jul 14, 2016, at 5:52 PM, Dan Appel <[email protected] >> <mailto:[email protected]>> wrote: >> >> Yes, it should be made clear that the 'async'/'await' suggestions are future >> directions, which are more to show how flexible the design is rather than >> actually be a part of the implementation. >> >> On Thu, Jul 14, 2016 at 3:30 PM Charles Srstka via swift-evolution >> <[email protected] <mailto:[email protected]>> wrote: >> Right, but since this would affect the Obj-C importer and thus would be a >> source-breaking change, it would probably not be possible anymore after >> Swift 3. >> >> Charles >> >> > On Jul 14, 2016, at 4:57 PM, Dan Stenmark <[email protected] >> > <mailto:[email protected]>> wrote: >> > >> > I’d say it’s a little premature to be talking about this; the team has >> > made it very clear that the discussion on Native Concurrency in Swift >> > won’t begin for another couple months. >> > >> > Dan >> > >> >> On Jul 14, 2016, at 10:54 AM, Charles Srstka via swift-evolution >> >> <[email protected] <mailto:[email protected]>> wrote: >> >> >> >> I know it’s late, but I was wondering what the community thought of this: >> >> >> >> MOTIVATION: >> >> >> >> With the acceptance of SE-0112, the error handling picture looks much >> >> stronger for Swift 3, but there is still one area of awkwardness >> >> remaining, in the area of returns from asynchronous methods. >> >> Specifically, many asynchronous APIs in the Cocoa framework are declared >> >> like this: >> >> >> >> - (void)doSomethingWithFoo: (Foo *)foo completionHandler: (void (^)(Bar * >> >> _Nullable, NSError * _Nullable))completionHandler; >> >> >> >> This will get imported into Swift as something like this: >> >> >> >> func doSomething(foo: Foo, completionHandler: (Bar?, Error?) -> ()) >> >> >> >> The intention of this API is that either the operation will succeed, and >> >> something will be passed in the Bar parameter, and the error will be nil, >> >> or else the operation will fail, and then the error parameter will be >> >> populated while the Bar parameter is nil. However, this intention is not >> >> expressed in the API, since the syntax leaves the possibility that both >> >> parameters could be nil, or that they could both be non-nil. This forces >> >> the developer to do needless and repetitive checks against a case which >> >> in practice shouldn’t occur, as below: >> >> >> >> doSomething(foo: foo) { bar, error in >> >> if let bar = bar { >> >> // handle success case >> >> } else if let error = error { >> >> self.handleError(error) >> >> } else { >> >> self.handleError(NSCocoaError.FileReadUnknownError) >> >> } >> >> } >> >> >> >> This results in the dreaded “untested code.” >> >> >> >> Note that while it is possible that the developer could simply >> >> force-unwrap error in the failure case, this leaves the programs open to >> >> crashes in the case where a misbehaved API forgets to populate the error >> >> on failure, whereas some kind of default error would be more appropriate. >> >> The do/try/catch mechanism works around this by returning a generic >> >> _NilError in cases where this occurs. >> >> >> >> PROPOSED SOLUTION: >> >> >> >> Since the pattern for an async API that returns an error in the Cocoa >> >> APIs is very similar to the pattern for a synchronous one, we can handle >> >> it in a very similar way. To do this, we introduce a new Result enum >> >> type. We then bridge asynchronous Cocoa APIs to return this Result type >> >> instead of optional values. This more clearly expresses to the user the >> >> intent of the API. >> >> >> >> In addition to clarifying many Cocoa interfaces, this will provide a >> >> standard format for asynchronous APIs that return errors, opening the way >> >> for these APIs to be seamlessly integrated into future asynchronous >> >> features added to Swift 4 and beyond, in a way that could seamlessly >> >> interact with the do/try/catch feature as well. >> >> >> >> DETAILED DESIGN: >> >> >> >> 1. We introduce a Result type, which looks like this: >> >> >> >> enum Result<T> { >> >> case success(T) >> >> case error(Error) >> >> } >> >> >> >> 2. Methods that return one parameter asynchronously with an error are >> >> bridged like this: >> >> >> >> func doSomething(foo: Foo, completionHandler: (Result<Bar>) -> ()) >> >> >> >> and are used like this: >> >> >> >> doSomething(foo: foo) { result in >> >> switch result { >> >> case let .success(bar): >> >> // handle success >> >> case let .error(error): >> >> self.handleError(error) >> >> } >> >> } >> >> >> >> 3. Methods that return multiple parameters asynchronously with an error >> >> are bridged using a tuple: >> >> >> >> func doSomething(foo: Foo, completionHandler: (Result<(Bar, Baz)>) -> ()) >> >> >> >> and are used like this: >> >> >> >> doSomething(foo: foo) { result in >> >> switch result { >> >> case let .success(bar, baz): >> >> // handle success >> >> case let .error(error): >> >> self.handleError(error) >> >> } >> >> } >> >> >> >> 4. Methods that return only an error and nothing else are bridged as they >> >> are currently, with the exception of bridging NSError to Error as in >> >> SE-0112: >> >> >> >> func doSomething(foo: Foo, completionHandler: (Error?) -> ()) >> >> >> >> and are used as they currently are: >> >> >> >> doSomething(foo: foo) { error in >> >> if let error = error { >> >> // handle error >> >> } else { >> >> // handle success >> >> } >> >> } >> >> >> >> 5. For the case in part 2, the bridge works much like the do/try/catch >> >> mechanism. If the first parameter is non-nil, it is returned inside the >> >> .success case. If it is nil, then the error is returned inside the .error >> >> case if it is non-nil, and otherwise _NilError is returned in the .error >> >> case. >> >> >> >> 6. For the case in part 3, in which there are multiple return values, the >> >> same pattern is followed, with the exception that we introduce a new >> >> Objective-C annotation. I am provisionally naming this annotation >> >> NS_REQUIRED_RETURN_VALUE, but the developer team can of course rename >> >> this annotation to whatever they find appropriate. All parameters >> >> annotated with NS_REQUIRED RETURN_VALUE will be required to be non-nil in >> >> order to avoid triggering the error case. Parameters not annotated with >> >> NS_REQUIRED RETURN_VALUE will be inserted into the tuple as optionals. If >> >> there are no parameters annotated with NS_REQUIRED RETURN_VALUE, the >> >> first parameter will be implicitly annotated as such. This allows >> >> asynchronous APIs to continue to return optional secondary values if >> >> needed. >> >> >> >> Thus, the following API: >> >> >> >> - (void)doSomethingWithFoo: (Foo *)foo completionHandler: (void (^)(Bar * >> >> _Nullable NS_REQUIRED_RETURN_VALUE, Baz * _Nullable >> >> NS_REQUIRED_RETURN_VALUE, NSError * _Nullable))completionHandler; >> >> >> >> is bridged as: >> >> >> >> func doSomething(foo: Foo, completionHandler: (Result<(Bar, Baz)>) -> ()) >> >> >> >> returning .success only if both the Bar and Baz parameters are non-nil, >> >> whereas this API: >> >> >> >> - (void)doSomethingWithFoo: (Foo *)foo completionHandler: (void (^)(Bar * >> >> _Nullable NS_REQUIRED_RETURN_VALUE, Baz * _Nullable, NSError * >> >> _Nullable))completionHandler; >> >> >> >> is bridged as: >> >> >> >> func doSomething(foo: Foo, completionHandler: (Result<(Bar, Baz?)>) -> ()) >> >> >> >> returning .success whenever the Bar parameter is nil. An API containing >> >> no parameter annotated with NS_REQUIRED_RETURN_VALUE will be bridged the >> >> same as above. >> >> >> >> FUTURE DIRECTIONS: >> >> >> >> In the future, an asynchronous API returning a Result could be bridged to >> >> an async function, should those be added in the future, using the >> >> semantics of the do/try/catch mechanism. The bridging would be additive, >> >> similarly to how Objective-C properties declared via manually written >> >> accessor methods can nonetheless be accessed via the dot syntax. Thus, >> >> >> >> func doSomething(_ completionHandler: (Result<Foo>) -> ()) >> >> >> >> could be used as if it were declared like this: >> >> >> >> async func doSomething() throws -> Foo >> >> >> >> and could be used like so: >> >> >> >> async func doSomethingBigger() { >> >> do { >> >> let foo = try await doSomething() >> >> >> >> // do something with foo >> >> } catch { >> >> // handle the error >> >> } >> >> } >> >> >> >> making asynchronous APIs convenient to write indeed. >> >> >> >> ALTERNATIVES CONSIDERED: >> >> >> >> Leaving the somewhat ambiguous situation as is. >> >> >> >> Charles >> >> >> >> _______________________________________________ >> >> swift-evolution mailing list >> >> [email protected] <mailto:[email protected]> >> >> https://lists.swift.org/mailman/listinfo/swift-evolution >> >> <https://lists.swift.org/mailman/listinfo/swift-evolution> >> > >> >> _______________________________________________ >> swift-evolution mailing list >> [email protected] <mailto:[email protected]> >> https://lists.swift.org/mailman/listinfo/swift-evolution >> <https://lists.swift.org/mailman/listinfo/swift-evolution> >> -- >> Dan Appel > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
