<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 Feb 23, 2017, at 10:58 AM, Anton Zhilin &lt;<a href="mailto:antonyzhilin@gmail.com" class="">antonyzhilin@gmail.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class="">See some inline response below.<div class="">Also, have you seen the issue I posted in Proposal thread? There is a way to create an instance of "any" type.<br class=""></div></div></div></blockquote><div><br class=""></div><div>Yes, I saw that. &nbsp;There is no problem with that at all. &nbsp;As I point out in the analysis below, rethrowing functions are allowed to throw any error they want. &nbsp;They are only limited by *where* they may throw.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><div class="gmail_extra"><br class=""><div class="gmail_quote">2017-02-23 3:37 GMT+03:00 Matthew Johnson via swift-evolution <span dir="ltr" class="">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>&gt;</span>:<br class=""><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"># Analysis of the design of typed throws<br class="">
<br class="">
## Problem<br class="">
<br class="">
There is a problem with how the proposal specifies `rethrows` for functions that take more than one throwing function.&nbsp; The proposal says that the rethrown type must be a common supertype of the type thrown by all of the functions it accepts.&nbsp; This makes some intuitive sense because this is a necessary bound if the rethrowing function lets errors propegate automatically - the rethrown type must be a supertype of all of the automatically propegated errors.<br class="">
<br class="">
This is not how `rethrows` actually works though.&nbsp; `rethrows` currently allows throwing any error type you want, but only in a catch block that covers a call to an argument that actually does throw and *does not* cover a call to a throwing function that is not an argument.&nbsp; The generalization of this to typed throws is that you can rethrow any type you want to, but only in a catch block that meets this rule.<br class="">
<br class="">
<br class="">
## Example typed rethrow that should be valid and isn't with this proposal<br class="">
<br class="">
This is a good thing, because for many error types `E` and `F` the only common supertype is `Error`.&nbsp; In a non-generic function it would be possible to create a marker protocol and conform both types and specify that as a common supertype.&nbsp; But in generic code this is not possible.&nbsp; The only common supertype we know about is `Error`.&nbsp; The ability to catch the generic errors and wrap them in a sum type is crucial.<br class="">
<br class="">
I'm going to try to use a somewhat realistic example of a generic function that takes two throwing functions that needs to be valid (and is valid under a direct generalization of the current rules applied by `rethrows`).<br class="">
<br class="">
enum TransformAndAccumulateError&lt;E, F&gt; {<br class="">
&nbsp; &nbsp;case transformError(E)<br class="">
&nbsp; &nbsp;case accumulateError(F)<br class="">
}<br class="">
<br class="">
func transformAndAccumulate&lt;E, F, T, U, V&gt;(<br class="">
&nbsp; &nbsp;_ values: [T],<br class="">
&nbsp; &nbsp;_ seed: V,<br class="">
&nbsp; &nbsp;_ transform: T -&gt; throws(E) U,<br class="">
&nbsp; &nbsp;_ accumulate: throws (V, U) -&gt; V<br class="">
) rethrows(<wbr class="">TransformAndAccumulateError&lt;E, F&gt;) -&gt; V {<br class="">
&nbsp; &nbsp;var accumulator = seed<br class="">
&nbsp; &nbsp;try {<br class="">
&nbsp; &nbsp; &nbsp; for value in values {<br class="">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;accumulator = try accumulate(accumulator, transform(value))<br class="">
&nbsp; &nbsp; &nbsp; }<br class="">
&nbsp; &nbsp;} catch let e as E {<br class="">
&nbsp; &nbsp; &nbsp; throw .transformError(e)<br class="">
&nbsp; &nbsp;} catch let f as F {<br class="">
&nbsp; &nbsp; &nbsp; throw .accumulateError(f)<br class="">
&nbsp; &nbsp;}<br class="">
&nbsp; &nbsp;return accumulator<br class="">
}<br class="">
<br class="">
It doesn't matter to the caller that your error type is not a supertype of `E` and `F`.&nbsp; All that matters is that the caller knows that you don't throw an error if the arguments don't throw (not only if the arguments *could* throw, but that one of the arguments actually *did* throw).&nbsp; This is what rethrows specifies.&nbsp; The type that is thrown is unimportant and allowed to be anything the rethrowing function (`transformAndAccumulate` in this case) wishes.<br class=""></blockquote><div class=""><br class=""></div><div class="">Yes, upcasting is only one way (besides others) to convert to a common error type. That's what I had in mind, but I'll state it more explicitly.</div></div></div></div></div></div></blockquote><div><br class=""></div><div>The important point is that if you include `rethrows` it should not place any restrictions on the type that it throws when its arguments throw. &nbsp;All it does is prevent the function from throwing unless there is a dynamic guarantee that one of the arguments did in fact throw (which of course means if none of them can throw then the rethrowing function cannot throw either).</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><div class="gmail_extra"><div class="gmail_quote"><div class="">&nbsp;</div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
## Eliminating rethrows<br class="">
<br class="">
We have discussed eliminating `rethrows` in favor of saying that non-throwing functions have an implicit error type of `Never`.&nbsp; As you can see by the rules above, if the arguments provided have an error type of `Never` the catch blocks are unreachable so we know that the function does not throw.&nbsp; Unfortunately a definition of nonthrowing functions as functions with an error type of `Never` turns out to be too narrow.<br class="">
<br class="">
If you look at the previous example you will see that the only way to propegate error type information in a generic function that rethrows errors from two arguments with unconstrained error types is to catch the errors and wrap them with an enum.&nbsp; Now imagine both arguments happen to be non-throwing (i.e. they throw `Never`).&nbsp; When we wrap the two possible thrown values `Never` we get a type of `TransformAndAccumulateError&lt;<wbr class="">Never, Never&gt;`.&nbsp; This type is uninhabitable, but is quite obviously not `Never`.<br class="">
<br class="">
In this proposal we need to specify what qualifies as a non-throwing function.&nbsp; I think we should specifty this in the way that allows us to eliminate `rethrows` from the language.&nbsp; In order to eliminate `rethrows` we need to say that any function throwing an error type that is uninhabitable is non-throwing.&nbsp; I suggest making this change in the proposal.<br class="">
<br class="">
If we specify that any function that throws an uninhabitable type is a non-throwing function then we don't need rethrows.&nbsp; Functions declared without `throws` still get the implicit error type of `Never` but other uninhabitable error types are also considered non-throwing.&nbsp; This provides the same guarantee as `rethrows` does today: if a function simply propegates the errors of its arguments (implicitly or by manual wrapping) and all arguments have `Never` as their error type the function is able to preserve the uninhabitable nature of the wrapped errors and is therefore known to not throw.<br class=""></blockquote><div class=""><br class=""></div><div class="">Yes, any empty type should be allowed instead of just `Never`. That's a general solution to the ploblem with `rethrows` and multiple throwing parameters.</div></div></div></div></div></div></blockquote><div><br class=""></div><div>It looks like you clipped out the section "Why this solution is better” which showed how `rethrows` is not capable of correctly typing a function as non-throwing if it dynamically handles all of the errors thrown by its arguments. &nbsp;What do you think of that? &nbsp;In my opinion, it makes a strong case for eliminating rethrows and introducing the uninhabited type solution from the beginning.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><div class="gmail_extra"><div class="gmail_quote"><div class=""><br class=""></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">### Language support<br class="">
<br class="">
This appears to be a problem in search of a language solution.&nbsp; We need a way to transform one error type into another error type when they do not have a common supertype without cluttering our code and writing boilerplate propegation functions.&nbsp; Ideally all we would need to do is declare the appropriate converting initializers and everything would fall into place.<br class="">
<br class="">
One major motivating reason for making error conversion more ergonomic is that we want to discourage users from simply propegating an error type thrown by a dependency.&nbsp; We want to encourage careful consideration of the type that is exposed whether that be `Error` or something more specific.&nbsp; If conversion is cumbersome many people who want to use typed errors will resort to just exposing the error type of the dependency.<br class="">
<br class="">
The problem of converting one type to another unrelated type (i.e. without a supertype relationship) is a general one.&nbsp; It would be nice if the syntactic solution was general such that it could be taken advantage of in other contexts should we ever have other uses for implicit non-supertype conversions.<br class="">
<br class="">
The most immediate solution that comes to mind is to have a special initializer attribute `@implicit init(_ other: Other)`.&nbsp; A type would provide one implicit initializer for each implicit conversion it supports.&nbsp; We also allow enum cases to be declared `@implicit`.&nbsp; This makes the propegation in the previous example as simple as adding the `@implicit ` attribute to the cases of our enum:<br class="">
<br class="">
enum TransformAndAccumulateError&lt;E, F&gt; {<br class="">
&nbsp; &nbsp;@implicit case transformError(E)<br class="">
&nbsp; &nbsp;@implicit case accumulateError(F)<br class="">
}<br class="">
<br class="">
It is important to note that these implicit conversions *would not* be in effect throughout the program.&nbsp; They would only be used in very specific semantic contexts, the first of which would be error propegation.<br class="">
<br class="">
An error propegation mechanism like this is additive to the original proposal so it could be introduced later.&nbsp; However, if we believe that simply passing on the error type of a dependency is often an anti-pattern and it should be discouraged, it is a good idea to strongly consider introducing this feature along with the intial proposal.<br class=""></blockquote><div class=""><br class=""></div><div class="">Will add to Future work section.<br class=""></div></div></div></div></div>
</div></blockquote></div><br class=""></body></html>