<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Dec 21, 2015, at 3:00 PM, David Owens II <<a href="mailto:david@owensd.io" class="">david@owensd.io</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div class=""><blockquote type="cite" class=""><div class="">I understand that Rust is not doing implicit conversions, but the effect for the user is pretty much the same. The try macro is converting the underlying error to the type that can be propagated. As I stated, Swift is not Rust and deserves a different solution. </div><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><div class=""><br class=""></div><div class="">Nevertheless, that does not minimize the need to solve the problem. I maintain that the <b class="">problem </b>solved by the try macro is a significant one that is not addressed by the current proposal. I would really like to see it addressed one way or another.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><div class=""><br class=""></div><div class="">You could make it “nicer” by doing something like this:</div><div class=""><br class=""></div></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><div class=""><font face="Menlo" class="">try MyError.convertFrom(try funcThatThrowsAnErrorThatMustBeTranslatedItoMyPublishedError())</font></div></div></blockquote></div></div></blockquote><div class=""><br class=""></div><div class="">Can you elaborate on how you think this would work? If funcThatThrowsAnErrorThatMustBeTranslatedItoMyPublishedError actually throws it will be propagated to the next enclosing catch clause. MyError.convertFrom will not have a chance to do anything with it.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Here’s a full playground example (I’ve annotated in comments where the type of error could be described):</div><div class=""><br class=""></div></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><div class=""><font face="Menlo" class="">enum InternalError: ErrorType {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> case Internal(value: Int)</font></div></div><div class=""><div class=""><font face="Menlo" class="">}</font></div></div><div class=""><div class=""><font face="Menlo" class=""><br class=""></font></div></div><div class=""><div class=""><font face="Menlo" class="">enum PublishedError: ErrorType {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> static func from<T>(@autoclosure fn: () throws -> T) throws -> T {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> do {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> return try fn()</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> catch InternalError.Internal(let value) {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> throw PublishedError.Converted(value: value)</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> catch {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> fatalError("unsupported conversion")</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> </font></div></div><div class=""><div class=""><font face="Menlo" class=""> case Converted(value: Int)</font></div></div><div class=""><div class=""><font face="Menlo" class="">}</font></div></div><div class=""><div class=""><font face="Menlo" class=""><br class=""></font></div></div><div class=""><div class=""><font face="Menlo" class=""><br class=""></font></div></div><div class=""><div class=""><font face="Menlo" class="">func example() {</font></div></div><div class=""><div class=""><font face="Menlo" class=""><br class=""></font></div></div><div class=""><div class=""><font face="Menlo" class=""> func bad(value: Int) throws /* InternalError */ -> Int {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> if value % 2 == 0 { throw InternalError.Internal(value: value) }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> return value</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""><br class=""></font></div></div><div class=""><div class=""><font face="Menlo" class=""> func verbose(value: Int) throws /* PublishedError */ -> Int {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> do {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> return try bad(value)</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> catch InternalError.Internal(let value) {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> throw PublishedError.Converted(value: value)</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> catch {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> fatalError("unsupported conversion")</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> </font></div></div><div class=""><div class=""><font face="Menlo" class=""> func convert(value: Int) throws /* PublishedError */ -> Int {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> return try PublishedError.from(try bad(value))</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> </font></div></div><div class=""><div class=""><font face="Menlo" class=""> do {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> let r1 = try verbose(11)</font></div></div><div class=""><div class=""><font face="Menlo" class=""> print("verbose: \(r1)")</font></div></div><div class=""><div class=""><font face="Menlo" class=""> </font></div></div><div class=""><div class=""><font face="Menlo" class=""> let r2 = try convert(9)</font></div></div><div class=""><div class=""><font face="Menlo" class=""> print("converted: \(r2)")</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> catch {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> print("error: \(error)")</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""><br class=""></font></div></div><div class=""><div class=""><font face="Menlo" class="">}</font></div></div><div class=""><div class=""><font face="Menlo" class=""><br class=""></font></div></div><div class=""><div class=""><font face="Menlo" class="">example()</font></div></div></blockquote><div class=""><div class=""><br class=""></div><div class=""><br class=""></div><div class="">As you can see, the “verbose()” and the “from()” conversion are basically the same implementation. What I’m saying is that I believe you can simply do the explicit conversion yourself without much fanfare (compare the verbose() and convert() implementations).</div><div class=""><br class=""></div><div class="">In the implementation of PublishedError.from() you can use Swift’s pattern matching to do all of your conversions in a single place. Note that where the implementation of “from” is at doesn’t matter, it could be on another type or a free function, whatever.</div></div></div></div></blockquote><div><br class=""></div><div>That is a pretty clever use of @autoclosure! It can be made even be made even more concise with typed errors and a top level conversion function:</div><div><br class=""></div><div>@protocol ErrorTypeConvertible {</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>// implementations will have to include a default clause which is either going to call fatalError </div><div> // or be an ‘UnknownError’ case in the enum</div><div><span class="Apple-tab-span" style="white-space: pre;">        </span>init(underlyingError: ErrorType) { … }</div><div> // or</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>init<E: ErrorType>(underlyingError: E) { … } </div><div>}</div><div><br class=""></div><div>func from<T/*, Internal, Published: ErrorTypeConvertible*/>(@autoclosure fn: () throws /* Internal */ -> T) throws /* Published */ -> T {<br class=""> do {<br class=""> return try fn()<br class=""> }<br class=""> catch let error as Internal {<br class=""> return Published(underlyingError: error)<br class=""> }</div><div> // hopefully the compiler is able to detect that this is <br class="">}</div><div><br class=""></div><div> func convert(value: Int) throws /* PublishedError */ -> Int {<br class=""> return try from(try bad(value))<br class=""> }</div><div><br class=""></div><div>This addresses my largest concern which is cluttering up the control flow. The additional noise of an extra ‘try' and a call to ‘from’ isn’t great, but it is tolerable, at least initially (I think we would eventually learn that it is just noise and get rid of it). </div><div><br class=""></div><div>Unfortunately, I don’t see a way to make it safe. You had to use fatalError in a default case to make it work. An alternative would have been to include an ‘UnknownError’ case in ‘PublishedError’. Neither is not an acceptable solution IMO.</div><div><br class=""></div><div>If you can make PublishedError.from safe without requiring an ‘UnknownError’ case it will also be possible to make a top-level ‘from’ safe. That would be acceptable, but I don’t believe it’s possible in the current language and I’m not aware of any proposed changes that would make it possible.</div><div><br class=""></div><div>This top level `from` example also brings up a couple of points that I don’t recall being addressed in your proposal. Specifically, the interaction of typed errors with generics and type inferrence. The obvious thing to do is allow them to behave the same as any other part of the return type. If that is what you expect and it is not already stated, you should update the proposal to specify that. If you expect something different you <i class="">definitely</i> need to specify what that is. An implementer will need to know how this should be handled.</div><div><br class=""></div><div>I still consider this to be an unresolved concern. I would like to have a <b class="">safe</b> way to perform error conversion during propagation without cluttering up my control flow and seriously degrading readability. This is a problem that <i class="">can</i> and <i class="">has</i> been solved in other languages. IMO it is should be considered an essential element of a proposal introducing typed errors.</div><div><br class=""></div><div>Matthew</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><div class=""><br class=""></div><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Are you willing to explore adding *explicit* syntax to convert thrown errors to your proposal? That seems like it might be a reasonable compromise between implicit conversions and manual boilerplate. </div></div></blockquote><div class=""><br class=""></div><div class="">The only boiler plate I’m seeing is the explicit conversion call: PublishedError.from(try bad(value))</div><div class=""><br class=""></div><div class="">Am I misunderstanding something? </div><div class=""><br class=""></div><div class="">To me, this would be much more confusing:</div><div class=""><br class=""></div><div class=""><div class=""><div class=""><font face="Menlo" class=""> func convert(value: Int) throws /* PublishedError */ -> Int {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> return try bad(value) /* implicit conversion from InternalError -> PublishedError */</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""></div></div><div class=""><br class=""></div><div class="">If there were implicit type conversions, this would have to be something that Swift supported all up. I’d be very hesitant to make this work for only errors. For example, how does implicit conversion work if we can later extend this to async behaviors? Do we have special conversions that can take an async error make it a synchronous error? How about vice-versa?</div><div class=""><br class=""></div><div class="">I guess I wouldn’t want to go further than having explicit conversions until we better understood all of those answers and how implicit type conversion would work in Swift generally. If I recall, Swift had implicit type conversion in the early versions, and it has been removed in most places. </div><br class=""></div><div class="">-David</div><br class=""></div></div></blockquote></div><br class=""></body></html>