[swift-evolution] [Review] SE-0112: Improved NSError Bridging
Douglas Gregor
dgregor at apple.com
Sun Jul 3 18:33:31 CDT 2016
> On Jul 1, 2016, at 6:25 PM, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote:
>
>> * What is your evaluation of the proposal?
>
> Massively in favor overall. I use NSError features heavily in Objective-C, and I'll be very, very glad to get powerful error bridging in Swift.
>
> I don't think it's mentioned anywhere, but are the new protocols in Foundation or Stdlib? I'm hoping they're in Foundation, because I consider localization to be a Foundation-level concern.
Yes, they’re all in Foundation.
>
> I have a number of critiques:
>
> * I think we need the word "localized" on the properties of `LocalizedError`. The fact that they're localized is a very important semantic, and I don't think the fact that they come from a protocol called "Localized" is enough to signal that at use sites. (With "localized" added, `errorDescription` would no longer need the vacuous word "error" to avoid colliding with `CustomStringConvertible`.)
To a close approximation, there are no use sites of these protocols. Error types will conform to this protocol to provide more information, and we expect all of these to be, e.g.,
extension MyError : LocalizedError { … }
where we don’t need the repeated “localized”.
For the very few that might want to handle a localized error on their own, this will be in the context of
if let localizedError = theError as? LocalizedError { … }
and again I think the context is clear.
>
> * I'm not sure about including the `helpAnchor` property in `LocalizedError`. It's not even relevant on three of *Apple's* four platforms, let alone on others. Could it be separated?
It’s defaulted; why bother to split it into a separate protocol?
>
> * I would like to see more work on `RecoverableError`. I know this is a more or less faithful translation of `NSErrorRecoveryAttempting`, but I don't think that's a particularly good API, and I would like to see it revisited.
>
> The design I'd prefer would look more like this:
>
> protocol RecoverableError: Error {
> var recoveryOptions: [ErrorRecoveryOption] { get }
> }
>
> typealias ErrorRecoveryCompletionHandler = (recovered: Bool) -> Void
>
> protocol ErrorRecoveryOption {
> var localizedName: String { get }
> func attemptRecovery(completion: ErrorRecoveryCompletionHandler) {…}
> }
>
> struct AnyErrorRecoveryOption: ErrorRecoveryOption {
> typealias RecoveryAttempter = (ErrorRecoveryCompletionHandler) -> Void
>
> var localizedName: String
> var attempter: RecoveryAttempter
>
> init(localizedName: String, attempter: RecoveryAttempter) {…}
>
> func attemptRecovery(completion: CompletionHandler) { attempter(completion) }
> }
>
> Though further from the equivalent Foundation API, I think this is a much cleaner design. It requires no `switch` statements, avoids issues with matching indices between the option array and the recovery attempter, and makes it easier for interested subtypes to add or remove recovery options.
I agree that we could probably do better with the design, although this feels a bit heavy with the introduction of the ErrorRecoveryOption protocol. The common case is probably an enum where each case describes an option, but I don’t have a natural way to model that.
> In particular, in my view, there should be only one way to attempt recovery, and that way should not be application-modal. If a client wants to recover modally, we should invoke the non-modal API and then spin the runloop until the completion handler runs.
Hrm. I’d very much prefer to let the system frameworks handle this, but that’s partly because I don’t have a lot of experience to fall back on with recovering from errors in Cocoa.
>
> If a better `RecoverableError` design isn't feasible in Swift 3, I think we can defer it. In a pinch, `CustomNSError` provides the ability to specify error-handling behavior the old-fashioned way.
Sure, we could delay just this bit if we think some significantly better design is coming.
>
> * You show an AVError struct with a nested .Code enum. Do you envision people doing similar things for pure-Swift error types? If so, what would that look like, and could it be made more ergonomic? If not, what do you imagine a similarly full-featured error type would look like?
I wouldn’t expect to see this come up often, because error types are generally enums, and Swift enums can carry arbitrary data in their cases. Imported enums are different because we might need to be able to refer to the “raw” code type in other APIs.
- Doug
More information about the swift-evolution
mailing list