<html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><blockquote type="cite" class="">On Aug 18, 2017, at 11:09 AM, John McCall via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">I think you're right that wrapping errors is tightly related to an effective use of typed errors. You can do a reasonable job without language support (as has been discussed on the list in the past). On the other hand, if we're going to introduce typed errors we should do it in a way that *encourages* effective use of them. My opinion is that encouraging effect use means categorizing (wrapping) errors without requiring any additional syntax beyond the simple `try` used by untyped errors. In practice, this means we should not need to catch and rethrow an error if all we want to do is categorize it. Rust provides good prior art in this area.<br class=""></blockquote><br class="">Yes, the ability to translate errors between domains is definitely something we could work on, whether we have typed errors or not.<br class=""></blockquote><br class=""><div class="">The Rust approach of automatically wrapping errors when you "cross domains", so to speak, has the disadvantage you've observed before that the layers of wrapping can obscure the structure of the underlying error when you're trying to ferret out and handle a particular form of failure mode. An alternative approach that embraces the open nature of errors could be to represent domains as independent protocols, and extend the error types that are relevant to that domain to conform to the protocol. That way, you don't obscure the structure of the underlying error value with wrappers. If you expect to exhaustively handle all errors in a domain, well, you'd almost certainly going to need to have a fallback case in your wrapper type for miscellaneous errors, but you could represent that instead without wrapping via a catch-all, and as?-casting to your domain protocol with a ??-default for errors that don't conform to the protocol. For example, instead of attempting something like this:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">enum DatabaseError {</div><div class=""> case queryError(QueryError)</div><div class=""> case ioError(IOError)</div><div class=""> case other(Error)</div><div class=""><br class=""></div><div class=""> var errorKind: String {</div><div class=""> switch self {</div><div class=""> case .queryError(let q): return "query error: \(q.query)"</div><div class=""> case .ioError(let i): return "io error: \(i.filename)"</div><div class=""> case .other(let e): return "\(e)"</div><div class=""> }</div><div class=""> }</div><div class="">}</div><div class=""><br class=""></div><div class="">func queryDatabase(_ query: String) throws /*DatabaseError*/ -> Table</div><div class=""><br class=""></div><div class="">do {</div><div class=""> queryDatabase("delete * from users")</div><div class="">} catch let d as DatabaseError {</div><div class=""> os_log(d.errorKind)</div><div class="">} catch {</div><div class=""> fatalError("unexpected non-database error")</div><div class="">}</div><div class=""><br class=""></div></blockquote>You could do this:<div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">protocol DatabaseError {</div><div class=""> var errorKind: String { get }</div><div class="">}</div><div class=""><br class=""></div><div class="">extension QueryError: DatabaseError {</div><div class=""> var errorKind: String { return "query error: \(q.query)" }</div><div class="">}</div><div class="">extension IOError: DatabaseError {</div><div class=""> var errorKind: String ( return "io error: \(i.filename)" }</div><div class="">}</div><div class=""><br class=""></div><div class="">extension Error {</div><div class=""> var databaseErrorKind: String {</div> return (error as? DatabaseError)?.errorKind ?? "unexpected non-database error"<div class=""> }</div><div class="">}</div><div class=""><br class=""></div><div class=""><div class="">func queryDatabase(_ query: String) throws -> Table</div></div><div class=""><br class=""></div><div class="">do {</div><div class=""> queryDatabase("delete * from users")</div><div class="">} catch {</div><div class=""> os_log(error.databaseErrorKind)</div><div class="">}</div><div class=""><br class=""></div></blockquote>-Joe</body></html>