Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 12:44 PM, Dave Abrahams  wrote:
> 
> 
> on Tue Feb 28 2017, Karl Wagner  wrote:
> 
>>> On 28 Feb 2017, at 18:00, Matthew Johnson  wrote:
>>> 
>>> 
 On Feb 28, 2017, at 10:44 AM, Daniel Leping > > wrote:
 
>> 
 Mathew, I totally understand what you're talking about. And this is 
 exactly what I want to avoid.
 
 If a library developer knows how to handle certain case
 (i.e. recover or throw a special error) I'm ok with it. Though I'm
 not ok with completely hiding errors from the underlying
 implementation. The library developer might not handle
 EVERYTHING. I want to know what happened and where. Otherwise I
 will have to explicitly debug his library to find out what the
 issue is. In most cases I will not have time to wait developer
 fixing the library. I will fix it myself and make PR. So I consider
 tranparent errors rather a benefit.
 
 Hiding errors under library errors might have worked for
 proprietary middleware. Today the world changed. It's open source
 now.
>>> 
>>> I agree with you that having *access* to the original error is
>>> important and that a library should not be expected to handle every
>>> case.  I hope I have not said anything that suggests otherwise.
>>> 
>>> What I’m suggesting is that if the error originated in a dependency
>>> that might change it should be wrapped in a stable abstraction that
>>> the users of the library can depend on.  The underlying error should
>>> still be available via a property or associated value exposed with
>>> an existential type.  If you expect users to catch errors thrown by
>>> a dependency that might change you have a fragile API contract in
>>> the area of errors.
>>> 
>>> This can easily lead to unreliable software - when you change the
>>> dependency nothing alerts users to the fact that their error
>>> handling code is probably broken.  Even if the compiler told them it
>>> was broken, they now have the burden of learning about the errors
>>> that might come from your new dependency.  An API that could lead to
>>> this consequence is badly designed IMO.
>>> 
>>> I even suggested adding a new requirement to `Error` that includes a
>>> default implementation to standardize how we access wrapped errors,
>>> including the originating error underneath all wrapper layers.
>>> 
>> 
>> I don’t see that it’s such a big problem. There are lots of ways to
>> expose the information once we have it, and it could easily be limited
>> to public errors from the current module (substituting private/unbound
>> errors with an erasing “Error”). There are lots of small improvements
>> we could make: such as having functions which re-throw errors from
>> other, manually documented functions inherit their documentation (or
>> at least appear to, when viewed). That would not be totally
>> compiler-generated documentation, but would mean you can document your
>> errors in one place and make doing so much more palatable. :
> 
> I just want to point at some prior art in this area,
> http://boost.org/libs/exception, which deals with the issues of wrapping
> errors at library boundaries and the dynamic construction of a stack of
> useful information.  This library was designed about 10 years ago, but I
> still remember the design discussions as compelling.  The formal review
> thread is here
> http://boost.2283326.n4.nabble.com/review-Review-of-Exception-begins-today-tt2633630.html#none
> but I think the most interesting discussion is probably in the foregoing
> thread
> http://boost.2283326.n4.nabble.com/Review-Request-Boost-Exception-tt2616607.html#a2616624
> (among which my comments are probably the *least* useful).
> 
> Anyone interested in pursuing features in this area should probably
> review those discussions.  Even though the context is C++, I think
> there's very little in it that's language-specific.

Thanks for the links Dave!  I will add this to my list of things to investigate 
further.

> 
> -- 
> -Dave

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 12:27 PM, Tino Heth via swift-evolution 
>  wrote:
> 
>> I disagree with that, it works if you only have a single function parameter 
>> type that throws an error, but if there are more than one inferring the type 
>> won’t be possible anymore: func foo(_: () throws(T) -> Void, _: () throws(S) 
>> -> Void) rethrows(S) (here, we’re assuming that T is handled inside foo).
>> 
> Well, isn't it actually incorrect that "rethrows" is a property of the 
> function, and not of its parameters?
> 
> Just imagine this
> 
> func catches(catchMe: () throws -> Void, throwMe: () throws -> Void) rethrows 
> {
>   try? catchMe()
>   try throwMe()
>   print("Nothing thrown")
> }
> 
> func doesThrow() throws {
>   throw NSError()
> }
> 
> func doesntThrow() {
>   print("I'm a nice function")
> }
> 
> try catches(catchMe: doesThrow, throwMe: doesntThrow)
> 
> The last call can't throw, but the compiler still needs the "try" on it.
> I guess in real-world code, this isn't an issue — but still, it's wrong… so 
> if we want a really sophisticated model for error handling, imho this should 
> be addressed.
> 
> Maybe we should also take into account that a higher-order function might 
> catch some errors, and only rethrow for special cases, and this adds 
> complexity as well?
> In this case, each function-parameter would require a full list a errors that 
> are filtered out, or even a translation-table… I guess most developers would 
> agree that this is overkill, and that keeping things simple has merit as well.

This is something I have discussed extensively in the thread titled “Analysis 
of the design of typed throws”.  I am interested if you have any comments on 
that discussion.

> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 12:21 PM, Daniel Leping  wrote:
> 
> Matthew, I understand your idea. Though IMO nested exceptions lead to 
> cluttering and to tons of boilerplate code.

I understand this and am suggesting that this boilerplate is a problem that can 
be addressed by language features.  We should not disregard the value of a 
stable error contract just because it currently requires boilerplate that could 
be eliminated.

> 
> What I think is that the important (logical) parts are to be provided by 
> custom errors (not wrapped, just custom).  It's a good practice and you don't 
> need any typed throws for it.

When the error is a result of a condition directly detected by the library this 
is true.  But if it was originally produced by an underlying error that 
information should not be thrown away.  It can be very useful, if for no other 
purpose than logging / debugging / troubleshooting.

You don’t need typed throws for wrappers either, it just allows you to 
communicate the wrapped error type to users when you decide that is 
appropriate.  When you decide that is providing too much concrete type 
information to callers you can still just throw `Error`.  In the latter case, 
you don’t need to wrap the errors even if you call other functions that can 
throw different error types.  `Error` is a supertype of all errors.

> Anyways, though, the library developer should change the major version 
> (everybody is more or less bound to same major version) and warn of the API 
> change. It's not unreliable API, it's sticking to the specific version. 
> Anyways the major versions are exactly the ones that break the API. What this 
> means is that the logical part remains the same, but some exceptions (all of 
> which you will anyways treat the same way as you don't know what to do with 
> them) should be transparent.

It’s best to minimize source-breaking changes even across major versions.  If 
you change a dependency and you are directly propagating errors from your 
dependencies it is source breaking for all callers who try to do anything more 
than totally generic “unknown error” handling in their code.  It is a bad idea 
to put users in the position of a) fragile error handling or b) totally generic 
error handling.

> 
> It's not that I don't understand you. I do. Though I had quite some 
> experience in Java (it has typed throws) and I know what problems this 
> approach inherently has. How do you think why Scala (modern language based on 
> Java) dropped typed throws even being supposed having them inherently?

I am not very familiar with Java and Scala but this topic has come up several 
times in the past and there have been several people who have commented on 
differences between the design in Java and the design in languages where it 
works better.

> 
> I think typed throws do more harm than good for all the reasons described 
> above.
> 
> TL;DR - typed throws is not a solution for the real world. It's good in 
> theory only.
> 
> On Tue, Feb 28, 2017 at 7:00 PM, Matthew Johnson  > wrote:
> 
>> On Feb 28, 2017, at 10:44 AM, Daniel Leping > > wrote:
>> 
>> Mathew, I totally understand what you're talking about. And this is exactly 
>> what I want to avoid.
>> 
>> If a library developer knows how to handle certain case (i.e. recover or 
>> throw a special error) I'm ok with it. Though I'm not ok with completely 
>> hiding errors from the underlying implementation. The library developer 
>> might not handle EVERYTHING. I want to know what happened and where. 
>> Otherwise I will have to explicitly debug his library to find out what the 
>> issue is. In most cases I will not have time to wait developer fixing the 
>> library. I will fix it myself and make PR. So I consider tranparent errors 
>> rather a benefit.
>> 
>> Hiding errors under library errors might have worked for proprietary 
>> middleware. Today the world changed. It's open source now.
> 
> I agree with you that having *access* to the original error is important and 
> that a library should not be expected to handle every case.  I hope I have 
> not said anything that suggests otherwise.
> 
> What I’m suggesting is that if the error originated in a dependency that 
> might change it should be wrapped in a stable abstraction that the users of 
> the library can depend on.  The underlying error should still be available 
> via a property or associated value exposed with an existential type.  If you 
> expect users to catch errors thrown by a dependency that might change you 
> have a fragile API contract in the area of errors.  
> 
> This can easily lead to unreliable software - when you change the dependency 
> nothing alerts users to the fact that their error handling code is probably 
> broken.  Even if the compiler told them it was broken, they now have the 
> burden of learning about the 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Dave Abrahams via swift-evolution

on Tue Feb 28 2017, Karl Wagner  wrote:

>> On 28 Feb 2017, at 18:00, Matthew Johnson  wrote:
>> 
>> 
>>> On Feb 28, 2017, at 10:44 AM, Daniel Leping  > wrote:
>>> 
>
>>> Mathew, I totally understand what you're talking about. And this is exactly 
>>> what I want to avoid.
>>> 
>>> If a library developer knows how to handle certain case
>>> (i.e. recover or throw a special error) I'm ok with it. Though I'm
>>> not ok with completely hiding errors from the underlying
>>> implementation. The library developer might not handle
>>> EVERYTHING. I want to know what happened and where. Otherwise I
>>> will have to explicitly debug his library to find out what the
>>> issue is. In most cases I will not have time to wait developer
>>> fixing the library. I will fix it myself and make PR. So I consider
>>> tranparent errors rather a benefit.
>>> 
>>> Hiding errors under library errors might have worked for
>>> proprietary middleware. Today the world changed. It's open source
>>> now.
>> 
>> I agree with you that having *access* to the original error is
>> important and that a library should not be expected to handle every
>> case.  I hope I have not said anything that suggests otherwise.
>> 
>> What I’m suggesting is that if the error originated in a dependency
>> that might change it should be wrapped in a stable abstraction that
>> the users of the library can depend on.  The underlying error should
>> still be available via a property or associated value exposed with
>> an existential type.  If you expect users to catch errors thrown by
>> a dependency that might change you have a fragile API contract in
>> the area of errors.
>> 
>> This can easily lead to unreliable software - when you change the
>> dependency nothing alerts users to the fact that their error
>> handling code is probably broken.  Even if the compiler told them it
>> was broken, they now have the burden of learning about the errors
>> that might come from your new dependency.  An API that could lead to
>> this consequence is badly designed IMO.
>> 
>> I even suggested adding a new requirement to `Error` that includes a
>> default implementation to standardize how we access wrapped errors,
>> including the originating error underneath all wrapper layers.
>> 
>
> I don’t see that it’s such a big problem. There are lots of ways to
> expose the information once we have it, and it could easily be limited
> to public errors from the current module (substituting private/unbound
> errors with an erasing “Error”). There are lots of small improvements
> we could make: such as having functions which re-throw errors from
> other, manually documented functions inherit their documentation (or
> at least appear to, when viewed). That would not be totally
> compiler-generated documentation, but would mean you can document your
> errors in one place and make doing so much more palatable. :

I just want to point at some prior art in this area,
http://boost.org/libs/exception, which deals with the issues of wrapping
errors at library boundaries and the dynamic construction of a stack of
useful information.  This library was designed about 10 years ago, but I
still remember the design discussions as compelling.  The formal review
thread is here
http://boost.2283326.n4.nabble.com/review-Review-of-Exception-begins-today-tt2633630.html#none
but I think the most interesting discussion is probably in the foregoing
thread
http://boost.2283326.n4.nabble.com/Review-Request-Boost-Exception-tt2616607.html#a2616624
(among which my comments are probably the *least* useful).

Anyone interested in pursuing features in this area should probably
review those discussions.  Even though the context is C++, I think
there's very little in it that's language-specific.

-- 
-Dave
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 11:55 AM, Karl Wagner  wrote:
> 
> 
>> On 28 Feb 2017, at 18:00, Matthew Johnson > > wrote:
>> 
>> 
>>> On Feb 28, 2017, at 10:44 AM, Daniel Leping >> > wrote:
>>> 
>>> Mathew, I totally understand what you're talking about. And this is exactly 
>>> what I want to avoid.
>>> 
>>> If a library developer knows how to handle certain case (i.e. recover or 
>>> throw a special error) I'm ok with it. Though I'm not ok with completely 
>>> hiding errors from the underlying implementation. The library developer 
>>> might not handle EVERYTHING. I want to know what happened and where. 
>>> Otherwise I will have to explicitly debug his library to find out what the 
>>> issue is. In most cases I will not have time to wait developer fixing the 
>>> library. I will fix it myself and make PR. So I consider tranparent errors 
>>> rather a benefit.
>>> 
>>> Hiding errors under library errors might have worked for proprietary 
>>> middleware. Today the world changed. It's open source now.
>> 
>> I agree with you that having *access* to the original error is important and 
>> that a library should not be expected to handle every case.  I hope I have 
>> not said anything that suggests otherwise.
>> 
>> What I’m suggesting is that if the error originated in a dependency that 
>> might change it should be wrapped in a stable abstraction that the users of 
>> the library can depend on.  The underlying error should still be available 
>> via a property or associated value exposed with an existential type.  If you 
>> expect users to catch errors thrown by a dependency that might change you 
>> have a fragile API contract in the area of errors.  
>> 
>> This can easily lead to unreliable software - when you change the dependency 
>> nothing alerts users to the fact that their error handling code is probably 
>> broken.  Even if the compiler told them it was broken, they now have the 
>> burden of learning about the errors that might come from your new 
>> dependency.  An API that could lead to this consequence is badly designed 
>> IMO.
>> 
>> I even suggested adding a new requirement to `Error` that includes a default 
>> implementation to standardize how we access wrapped errors, including the 
>> originating error underneath all wrapper layers.
>> 
> 
> I don’t see that it’s such a big problem. There are lots of ways to expose 
> the information once we have it, and it could easily be limited to public 
> errors from the current module (substituting private/unbound errors with an 
> erasing “Error”).

If you restrict it in this way then it will require wrapping if the caller has 
any meaningful way to handle errors thrown by your dependencies that is not 
“something went wrong (but I have no idea what it is or how to fix it)”.

> There are lots of small improvements we could make: such as having functions 
> which re-throw errors from other, manually documented functions inherit their 
> documentation (or at least appear to, when viewed). That would not be totally 
> compiler-generated documentation, but would mean you can document your errors 
> in one place and make doing so much more palatable. :
> 
> // Manual documentation in one place:
> 
> /// - throws: X, Y, Z
> func doSomething() throws { … }
> 
> // Inherited by functions which call it and rethrow its errors:
> 
> /// [generated]: - throws: X, Y, X
> func doSomethingExtended() throws {
> try doSomething()
> }
> 
> /// [generated]: - throws: X, Y, Z
> func doSomethingMapped() throws {
> // (call to rethrowing function ‘map’) -> (throwing closure expression) 
> -> (call to throwing function “doSomething”, maybe others, too...) -> X, Y, Z
> try [1, 2, 3].map { try doSomething() }
> }
> 
> Personally, I don’t like wrapping.

I don’t like the boilerplate wrapping can lead to but I think the semantics are 
often the right semantics.  If you don’t do this you either prevent callers 
from handling some errors with a more specific error path or you create 
unwanted coupling between your callers and your dependencies.

The right way to solve this (IMO) is to find ways to eliminate the boilerplate.

> I like error-handling to be flat at each level because it’s much easier to 
> reason about.

I like wrapping because it is often easier to reason about high-level groupings 
of underlying errors that have been thoughtfully designed than it is to reason 
about everything that can go wrong.  Often this will still result in flat error 
handling because the grouping is sufficient and the underlying error is 
irrelevant to how the error is handled.

> You would basically need catch on "error as? MyError” and 
> “error.underlyingError as? MyError”, duplicating code or forwarding them to a 
> refactored handler function.

Or we could introduce some syntax to allow you to ignore the wrapper when you 
really need to 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Tino Heth via swift-evolution
> I disagree with that, it works if you only have a single function parameter 
> type that throws an error, but if there are more than one inferring the type 
> won’t be possible anymore: func foo(_: () throws(T) -> Void, _: () throws(S) 
> -> Void) rethrows(S) (here, we’re assuming that T is handled inside foo).
> 
Well, isn't it actually incorrect that "rethrows" is a property of the 
function, and not of its parameters?

Just imagine this

func catches(catchMe: () throws -> Void, throwMe: () throws -> Void) rethrows {
try? catchMe()
try throwMe()
print("Nothing thrown")
}

func doesThrow() throws {
throw NSError()
}

func doesntThrow() {
print("I'm a nice function")
}

try catches(catchMe: doesThrow, throwMe: doesntThrow)

The last call can't throw, but the compiler still needs the "try" on it.
I guess in real-world code, this isn't an issue — but still, it's wrong… so if 
we want a really sophisticated model for error handling, imho this should be 
addressed.

Maybe we should also take into account that a higher-order function might catch 
some errors, and only rethrow for special cases, and this adds complexity as 
well?
In this case, each function-parameter would require a full list a errors that 
are filtered out, or even a translation-table… I guess most developers would 
agree that this is overkill, and that keeping things simple has merit as well.___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Daniel Leping via swift-evolution
Matthew, I understand your idea. Though IMO nested exceptions lead to
cluttering and to tons of boilerplate code.

What I think is that the important (logical) parts are to be provided by
custom errors (not wrapped, just custom). It's a good practice and you
don't need any typed throws for it. Anyways, though, the library developer
should change the major version (everybody is more or less bound to same
major version) and warn of the API change. It's not unreliable API, it's
sticking to the specific version. Anyways the major versions are exactly
the ones that break the API. What this means is that the logical part
remains the same, but some exceptions (all of which you will anyways treat
the same way as you don't know what to do with them) should be transparent.

It's not that I don't understand you. I do. Though I had quite some
experience in Java (it has typed throws) and I know what problems this
approach inherently has. How do you think why Scala (modern language based
on Java) dropped typed throws even being supposed having them inherently?

I think typed throws do more harm than good for all the reasons described
above.

TL;DR - typed throws is not a solution for the real world. It's good in
theory only.

On Tue, Feb 28, 2017 at 7:00 PM, Matthew Johnson 
wrote:

>
> On Feb 28, 2017, at 10:44 AM, Daniel Leping 
> wrote:
>
> Mathew, I totally understand what you're talking about. And this is
> exactly what I want to avoid.
>
> If a library developer knows how to handle certain case (i.e. recover or
> throw a special error) I'm ok with it. Though I'm not ok with completely
> hiding errors from the underlying implementation. The library developer
> might not handle EVERYTHING. I want to know what happened and where.
> Otherwise I will have to explicitly debug his library to find out what the
> issue is. In most cases I will not have time to wait developer fixing the
> library. I will fix it myself and make PR. So I consider tranparent errors
> rather a benefit.
>
> Hiding errors under library errors might have worked for proprietary
> middleware. Today the world changed. It's open source now.
>
>
> I agree with you that having *access* to the original error is important
> and that a library should not be expected to handle every case.  I hope I
> have not said anything that suggests otherwise.
>
> What I’m suggesting is that if the error originated in a dependency that
> might change it should be wrapped in a stable abstraction that the users of
> the library can depend on.  The underlying error should still be available
> via a property or associated value exposed with an existential type.  If
> you expect users to catch errors thrown by a dependency that might change
> you have a fragile API contract in the area of errors.
>
> This can easily lead to unreliable software - when you change the
> dependency nothing alerts users to the fact that their error handling code
> is probably broken.  Even if the compiler told them it was broken, they now
> have the burden of learning about the errors that might come from your new
> dependency.  An API that could lead to this consequence is badly designed
> IMO.
>
> I even suggested adding a new requirement to `Error` that includes a
> default implementation to standardize how we access wrapped errors,
> including the originating error underneath all wrapper layers.
>
>
> My point is that I'm completely against typed throws. I'm sure there will
> be a lot of developers who would try to use the feature to hide underlying
> errors under their own. I can't consider it a good practice (reasons
> above). This is how good intentions (caring about API and library users)
> can lead to bad consequences. Sounds good in theory - works bad in practice
> (in real, non ideal world).
>
>
> What is your opinion about the problem I describe above?  Do you think
> this is unimportant?
>
>
> On Tue, 28 Feb 2017 at 18:18 Matthew Johnson 
> wrote:
>
>>
>> On Feb 28, 2017, at 5:09 AM, Daniel Leping 
>> wrote:
>>
>> When you're going to present a user with specific error message you don't
>> do it for all errors. First of all at the end of a call chain you will have
>> a couple of dozens of possible errors and only 2 or 3 will do really matter
>> for the end user. Would you list all the dozen in params? Probably not.
>> You'll just catch what matters and put the rest to the log with a default
>> "sorry" message.
>>
>> What I mean is it's OK to be "unaware" why everything failed. What really
>> does matter is what you can deal with and what you can't (the rest). "The
>> rest" is usually a magnitude bigger, though.
>>
>> I might agree, that listing some errors in the throws clause is useful
>> feature for documentation (even so, I think the docs should be in the
>> comments), but only and only if it can throw the listed errors + a bunch of
>> other stuff. In Java it's resolved with a 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Karl Wagner via swift-evolution

> On 28 Feb 2017, at 18:00, Matthew Johnson  wrote:
> 
> 
>> On Feb 28, 2017, at 10:44 AM, Daniel Leping > > wrote:
>> 
>> Mathew, I totally understand what you're talking about. And this is exactly 
>> what I want to avoid.
>> 
>> If a library developer knows how to handle certain case (i.e. recover or 
>> throw a special error) I'm ok with it. Though I'm not ok with completely 
>> hiding errors from the underlying implementation. The library developer 
>> might not handle EVERYTHING. I want to know what happened and where. 
>> Otherwise I will have to explicitly debug his library to find out what the 
>> issue is. In most cases I will not have time to wait developer fixing the 
>> library. I will fix it myself and make PR. So I consider tranparent errors 
>> rather a benefit.
>> 
>> Hiding errors under library errors might have worked for proprietary 
>> middleware. Today the world changed. It's open source now.
> 
> I agree with you that having *access* to the original error is important and 
> that a library should not be expected to handle every case.  I hope I have 
> not said anything that suggests otherwise.
> 
> What I’m suggesting is that if the error originated in a dependency that 
> might change it should be wrapped in a stable abstraction that the users of 
> the library can depend on.  The underlying error should still be available 
> via a property or associated value exposed with an existential type.  If you 
> expect users to catch errors thrown by a dependency that might change you 
> have a fragile API contract in the area of errors.  
> 
> This can easily lead to unreliable software - when you change the dependency 
> nothing alerts users to the fact that their error handling code is probably 
> broken.  Even if the compiler told them it was broken, they now have the 
> burden of learning about the errors that might come from your new dependency. 
>  An API that could lead to this consequence is badly designed IMO.
> 
> I even suggested adding a new requirement to `Error` that includes a default 
> implementation to standardize how we access wrapped errors, including the 
> originating error underneath all wrapper layers.
> 

I don’t see that it’s such a big problem. There are lots of ways to expose the 
information once we have it, and it could easily be limited to public errors 
from the current module (substituting private/unbound errors with an erasing 
“Error”). There are lots of small improvements we could make: such as having 
functions which re-throw errors from other, manually documented functions 
inherit their documentation (or at least appear to, when viewed). That would 
not be totally compiler-generated documentation, but would mean you can 
document your errors in one place and make doing so much more palatable. :

// Manual documentation in one place:

/// - throws: X, Y, Z
func doSomething() throws { … }

// Inherited by functions which call it and rethrow its errors:

/// [generated]: - throws: X, Y, X
func doSomethingExtended() throws {
try doSomething()
}

/// [generated]: - throws: X, Y, Z
func doSomethingMapped() throws {
// (call to rethrowing function ‘map’) -> (throwing closure expression) -> 
(call to throwing function “doSomething”, maybe others, too...) -> X, Y, Z
try [1, 2, 3].map { try doSomething() }
}

Personally, I don’t like wrapping. I like error-handling to be flat at each 
level because it’s much easier to reason about. You would basically need catch 
on "error as? MyError” and “error.underlyingError as? MyError”, duplicating 
code or forwarding them to a refactored handler function. Also, consider that 
an Error might be any type at all (including a class). If any instance of a 
thing which conforms to Error could potentially be holding a reference-counted 
pointer, that could limit optimisations. If it’s flat, we can potentially prove 
that it has an exact type (or at least is never reference-counted), and that 
could be valuable.

Error-contracts in libraries are only really fixed in the case of an 
implementation which is never going to change substantially. In that case, the 
function is a possible candidate for @inlineable and can be treated as though 
part of the client’s module, with full error-flow intelligence.

- Karl

>> 
>> My point is that I'm completely against typed throws. I'm sure there will be 
>> a lot of developers who would try to use the feature to hide underlying 
>> errors under their own. I can't consider it a good practice (reasons above). 
>> This is how good intentions (caring about API and library users) can lead to 
>> bad consequences. Sounds good in theory - works bad in practice (in real, 
>> non ideal world).
> 
> What is your opinion about the problem I describe above?  Do you think this 
> is unimportant?
> 
>> 
>> On Tue, 28 Feb 2017 at 18:18 Matthew Johnson > 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 11:17 AM, Zaid Daghestani  wrote:
> 
> Matthew, Per api fragility, the only API fragility would likely be adding a 
> new error to the signature, which isn’t that bad. Any significant error 
> changes, would probably mirror a significant change in that libraries pattern 
> in and of itself, which would lend to a Semver.Major version bump anyhow.

Not at all.  If I use library A to do something and switch to library B the 
errors reported may be significantly different.  The point is not that you just 
need to add library B’s error types to your signature at all.  The point is 
that it is better to allow callers to handle the errors based on the general 
semantics of the cause of failure and not the specific type that a dependency 
threw.  You simply cannot do this without wrapping the errors.

> 
> It looks the consensus here is: 
>   Typed throws can be good, but wrapping kills dev cycles, and these 
> types can pollute our space.

The best answer to this is adding implicit conversion during propagation and 
enhancing the `Error` protocol to facilitate access to the underlying (wrapped) 
error.

> 
> To which, I think the obvious answer here is: 
>   Typed throws with inferred signatures.

I disagree.  Wrapping requires human judgement and intentional design.  There 
is very little value in just saying “here’s all the concrete error types my 
current implementation might throw”.

> 
> For the compiler to infer throw signatures is easily accessible and trivial 
> to accomplish. Xcode can even display the inferred signatures. The compiler 
> only need to add any `throw err` and `try erringFunc()` results to the throw 
> sig.
> 
> Implementing this gives us two gains at little cost: 
> 1) Error sets are now ready available to use, but we can still ignore if we 
> please!
> 2) No catch Error, unless we *explicitly* want a catch all

But this eliminates the human judgement that is required.  A well designed 
error propagation mechanism is a much better solution to allowing us to avoid 
the need to manually catch, wrap, and throw the errors we are propagating.  I 
*don’t want* the concrete error types exposed automatically.  That would 
*increase* the kind of fragile coupling that everyone has pointed out is a 
problem.

> 
> I honestly believe freedom is key when it comes to error implementation. 
> Environment usually dictates whether we fully implement error flows or not. 
> With these, we still maintain our wonderfully fast iteration cycles by 
> ignoring/catchall, but when we do implement errors, we get to fully leverage 
> it.
> 
> Everyone wins!
> 
> Cheers,
> Z
> 
> P.S. Stack traces in errors would be awesome. 
> 
>> On Feb 28, 2017, at 9:00 AM, Matthew Johnson via swift-evolution 
>> > wrote:
>> 
>> 
>>> On Feb 28, 2017, at 10:44 AM, Daniel Leping >> > wrote:
>>> 
>>> Mathew, I totally understand what you're talking about. And this is exactly 
>>> what I want to avoid.
>>> 
>>> If a library developer knows how to handle certain case (i.e. recover or 
>>> throw a special error) I'm ok with it. Though I'm not ok with completely 
>>> hiding errors from the underlying implementation. The library developer 
>>> might not handle EVERYTHING. I want to know what happened and where. 
>>> Otherwise I will have to explicitly debug his library to find out what the 
>>> issue is. In most cases I will not have time to wait developer fixing the 
>>> library. I will fix it myself and make PR. So I consider tranparent errors 
>>> rather a benefit.
>>> 
>>> Hiding errors under library errors might have worked for proprietary 
>>> middleware. Today the world changed. It's open source now.
>> 
>> I agree with you that having *access* to the original error is important and 
>> that a library should not be expected to handle every case.  I hope I have 
>> not said anything that suggests otherwise.
>> 
>> What I’m suggesting is that if the error originated in a dependency that 
>> might change it should be wrapped in a stable abstraction that the users of 
>> the library can depend on.  The underlying error should still be available 
>> via a property or associated value exposed with an existential type.  If you 
>> expect users to catch errors thrown by a dependency that might change you 
>> have a fragile API contract in the area of errors.  
>> 
>> This can easily lead to unreliable software - when you change the dependency 
>> nothing alerts users to the fact that their error handling code is probably 
>> broken.  Even if the compiler told them it was broken, they now have the 
>> burden of learning about the errors that might come from your new 
>> dependency.  An API that could lead to this consequence is badly designed 
>> IMO.
>> 
>> I even suggested adding a new requirement to `Error` that includes a default 
>> implementation to standardize how 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 11:08 AM, Vladimir.S  wrote:
> 
> On 28.02.2017 19:48, Matthew Johnson wrote:
>> 
>>> On Feb 28, 2017, at 6:47 AM, Vladimir.S  wrote:
>>> 
>>> On 28.02.2017 0:40, Matthew Johnson via swift-evolution wrote:
 
> On Feb 27, 2017, at 1:46 PM, David Waite via swift-evolution
> > wrote:
> 
> IMHO, there are two kinds of responses to errors - a specific response,
> and a general one. Only the calling code knows how it will deal with
> errors, so a “typed throws” is the function guessing possible calling
> code behavior.
> 
> The problem is, that gives four possible combinations - two where the
> function guesses correctly, and two where it doesn’t. The most damaging
> is when it suspects a caller doesn’t care about the error, when the
> caller actually does. This is unwanted wrapping.
> 
> To provide an example, imagine a library that parses JSON. It has several
> errors indicating JSON syntactic errors, and an “other” for representing
> errors on the input stream. It wraps the input stream errors so that it
> can provide a closed set of errors to the caller.
> 
> The caller is responsible for returning a data set. It doesn’t think that
> code calling ‘it” cares about JSON syntactic errors, merely that the
> object was not able to be restored. It returns its own wrapped error.
> 
> However, the original caller knows it is loading from disk. If the
> problem is due to an issue such as access permissions, It has to know
> implementation details of the API it called if it wishes to dive through
> the wrapped errors to find out if the problem was filesystem related.
> 
> Add more layers, and it can be very mysterious why a call failed. Java at
> least captures stack traces in this case to aid in technical support in
> diagnosing the error.
> 
> Wrapping exceptions also prevents an aggregate of errors from different
> subsystems being handled as a category, such as having a catch block
> handle RecoverableError generically
 
 Fwiw, I think wrapping errors is something that people are sometimes going
 to want to do regardless of whether they are typed or not.  Maybe the
 solution is to better support wrapping errors by focusing on the problems
 that wrapping causes.  For example, we could do something like this to make
>>> 
>>> Just to clarify, do you think about something like this? :
>>> (pseudocode, sorry for mistakes)
>> 
>> Is the question about the ability to create `fullErrorStackDescription`?  
>> Yes, that would certainly be possible to implement in an extension.
>> 
>> I’m not sure why your examples use `rethrow` to rethrow the error instead of 
>> `throw`.  Was that a mistake?
>> 
>> I also want to reiterate that deciding when and how to wrap errors is 
>> something that requires careful thought and judgment.  The goal is to 
>> provide a stable and ergonomic way for callers to learn about and handle 
>> errors they are likely to care about.  There will often be an “everything 
>> else” case and that’s ok.
>> 
>> As others have pointed out, the important thing is that it is easy to 
>> identify cases where I can *improve* UX by recovery or specific messaging to 
>> the user.  The fact that there will usually be a default path is a given, 
>> but it’s best to avoid that path if possible.
>> 
> 
> Well, I was trying to figure out how the some code could looks like with 
> typed throws, taking into account your suggestion about built-in 
> underlyingError and originalError props in Error protocol and the idea of 
> "Maybe the solution is to better support wrapping errors by focusing on the 
> problems that wrapping causes".
> As for 'rethrow' it is not a mistake, but hypothetical use of the keyword to 
> rethrow underlying error inside own error without boilerplate code, i.e. 
> "better support wrapping", compare:
> 
> catch let e {
>  // need to somehow inject the current e instance into our error instance
>  // (or with some other syntax)
>  throw BarError.fooRelatedError(SomeType1(), underlying: e)
> }
> 
> and
> 
> catch {
>  // 'rethrow' can clearly say that the current error instance will be injected
>  // into our own error instance in underlyingError prop. we focuse only
>  // on our own error instance
>  rethrow fooRelatedError(SomeType1())
> }
> 
> Sorry for not clarifying all this in first message.

Ahh, ok.  I think the best way to accomplish something like this would be with 
some kind of general error propagation, as has already been discussed.  With a 
mechanism like that you wouldn’t have to catch the error at all when you’re 
just wrapping it.

> 
>>> 
>>> func foo() throws {}
>>> 
>>> func bar() throws {}
>>> 
>>> enum BazError: Error { case baz1, case baz2 }
>>> func baz() throws(BazError) {..}
>>> 
>>> enum 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Zaid Daghestani via swift-evolution
Matthew, Per api fragility, the only API fragility would likely be adding a new 
error to the signature, which isn’t that bad. Any significant error changes, 
would probably mirror a significant change in that libraries pattern in and of 
itself, which would lend to a Semver.Major version bump anyhow.

It looks the consensus here is: 
Typed throws can be good, but wrapping kills dev cycles, and these 
types can pollute our space.

To which, I think the obvious answer here is: 
Typed throws with inferred signatures.

For the compiler to infer throw signatures is easily accessible and trivial to 
accomplish. Xcode can even display the inferred signatures. The compiler only 
need to add any `throw err` and `try erringFunc()` results to the throw sig.

Implementing this gives us two gains at little cost: 
1) Error sets are now ready available to use, but we can still ignore if we 
please!
2) No catch Error, unless we *explicitly* want a catch all

I honestly believe freedom is key when it comes to error implementation. 
Environment usually dictates whether we fully implement error flows or not. 
With these, we still maintain our wonderfully fast iteration cycles by 
ignoring/catchall, but when we do implement errors, we get to fully leverage it.

Everyone wins!

Cheers,
Z

P.S. Stack traces in errors would be awesome. 

> On Feb 28, 2017, at 9:00 AM, Matthew Johnson via swift-evolution 
>  wrote:
> 
> 
>> On Feb 28, 2017, at 10:44 AM, Daniel Leping > > wrote:
>> 
>> Mathew, I totally understand what you're talking about. And this is exactly 
>> what I want to avoid.
>> 
>> If a library developer knows how to handle certain case (i.e. recover or 
>> throw a special error) I'm ok with it. Though I'm not ok with completely 
>> hiding errors from the underlying implementation. The library developer 
>> might not handle EVERYTHING. I want to know what happened and where. 
>> Otherwise I will have to explicitly debug his library to find out what the 
>> issue is. In most cases I will not have time to wait developer fixing the 
>> library. I will fix it myself and make PR. So I consider tranparent errors 
>> rather a benefit.
>> 
>> Hiding errors under library errors might have worked for proprietary 
>> middleware. Today the world changed. It's open source now.
> 
> I agree with you that having *access* to the original error is important and 
> that a library should not be expected to handle every case.  I hope I have 
> not said anything that suggests otherwise.
> 
> What I’m suggesting is that if the error originated in a dependency that 
> might change it should be wrapped in a stable abstraction that the users of 
> the library can depend on.  The underlying error should still be available 
> via a property or associated value exposed with an existential type.  If you 
> expect users to catch errors thrown by a dependency that might change you 
> have a fragile API contract in the area of errors.  
> 
> This can easily lead to unreliable software - when you change the dependency 
> nothing alerts users to the fact that their error handling code is probably 
> broken.  Even if the compiler told them it was broken, they now have the 
> burden of learning about the errors that might come from your new dependency. 
>  An API that could lead to this consequence is badly designed IMO.
> 
> I even suggested adding a new requirement to `Error` that includes a default 
> implementation to standardize how we access wrapped errors, including the 
> originating error underneath all wrapper layers.
> 
>> 
>> My point is that I'm completely against typed throws. I'm sure there will be 
>> a lot of developers who would try to use the feature to hide underlying 
>> errors under their own. I can't consider it a good practice (reasons above). 
>> This is how good intentions (caring about API and library users) can lead to 
>> bad consequences. Sounds good in theory - works bad in practice (in real, 
>> non ideal world).
> 
> What is your opinion about the problem I describe above?  Do you think this 
> is unimportant?
> 
>> 
>> On Tue, 28 Feb 2017 at 18:18 Matthew Johnson > > wrote:
>> 
>>> On Feb 28, 2017, at 5:09 AM, Daniel Leping >> > wrote:
>>> 
>>> When you're going to present a user with specific error message you don't 
>>> do it for all errors. First of all at the end of a call chain you will have 
>>> a couple of dozens of possible errors and only 2 or 3 will do really matter 
>>> for the end user. Would you list all the dozen in params? Probably not. 
>>> You'll just catch what matters and put the rest to the log with a default 
>>> "sorry" message.
>>> 
>>> What I mean is it's OK to be "unaware" why everything failed. What really 
>>> does matter is what you can deal with and what you can't (the 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Vladimir.S via swift-evolution

On 28.02.2017 19:48, Matthew Johnson wrote:



On Feb 28, 2017, at 6:47 AM, Vladimir.S  wrote:

On 28.02.2017 0:40, Matthew Johnson via swift-evolution wrote:



On Feb 27, 2017, at 1:46 PM, David Waite via swift-evolution
> wrote:

IMHO, there are two kinds of responses to errors - a specific response,
and a general one. Only the calling code knows how it will deal with
errors, so a “typed throws” is the function guessing possible calling
code behavior.

The problem is, that gives four possible combinations - two where the
function guesses correctly, and two where it doesn’t. The most damaging
is when it suspects a caller doesn’t care about the error, when the
caller actually does. This is unwanted wrapping.

To provide an example, imagine a library that parses JSON. It has several
errors indicating JSON syntactic errors, and an “other” for representing
errors on the input stream. It wraps the input stream errors so that it
can provide a closed set of errors to the caller.

The caller is responsible for returning a data set. It doesn’t think that
code calling ‘it” cares about JSON syntactic errors, merely that the
object was not able to be restored. It returns its own wrapped error.

However, the original caller knows it is loading from disk. If the
problem is due to an issue such as access permissions, It has to know
implementation details of the API it called if it wishes to dive through
the wrapped errors to find out if the problem was filesystem related.

Add more layers, and it can be very mysterious why a call failed. Java at
least captures stack traces in this case to aid in technical support in
diagnosing the error.

Wrapping exceptions also prevents an aggregate of errors from different
subsystems being handled as a category, such as having a catch block
handle RecoverableError generically


Fwiw, I think wrapping errors is something that people are sometimes going
to want to do regardless of whether they are typed or not.  Maybe the
solution is to better support wrapping errors by focusing on the problems
that wrapping causes.  For example, we could do something like this to make


Just to clarify, do you think about something like this? :
(pseudocode, sorry for mistakes)


Is the question about the ability to create `fullErrorStackDescription`?  Yes, 
that would certainly be possible to implement in an extension.

I’m not sure why your examples use `rethrow` to rethrow the error instead of 
`throw`.  Was that a mistake?

I also want to reiterate that deciding when and how to wrap errors is something 
that requires careful thought and judgment.  The goal is to provide a stable 
and ergonomic way for callers to learn about and handle errors they are likely 
to care about.  There will often be an “everything else” case and that’s ok.

As others have pointed out, the important thing is that it is easy to identify 
cases where I can *improve* UX by recovery or specific messaging to the user.  
The fact that there will usually be a default path is a given, but it’s best to 
avoid that path if possible.



Well, I was trying to figure out how the some code could looks like with 
typed throws, taking into account your suggestion about built-in 
underlyingError and originalError props in Error protocol and the idea of 
"Maybe the solution is to better support wrapping errors by focusing on the 
problems that wrapping causes".
As for 'rethrow' it is not a mistake, but hypothetical use of the keyword 
to rethrow underlying error inside own error without boilerplate code, i.e. 
"better support wrapping", compare:


catch let e {
  // need to somehow inject the current e instance into our error instance
  // (or with some other syntax)
  throw BarError.fooRelatedError(SomeType1(), underlying: e)
}

and

catch {
  // 'rethrow' can clearly say that the current error instance will be 
injected

  // into our own error instance in underlyingError prop. we focuse only
  // on our own error instance
  rethrow fooRelatedError(SomeType1())
}

Sorry for not clarifying all this in first message.



func foo() throws {}

func bar() throws {}

enum BazError: Error { case baz1, case baz2 }
func baz() throws(BazError) {..}

enum BatError: Error {
 case fooRelatedError(SomeType1)
 case barOrBazRelatedError
 case specialErrorOne(Int)
 case specialErrorTwo(String)
}

func bat() throws(BatError) {
 do {
   try foo()
 }
 catch {
   // underlyingError will be injected
   rethrow .fooRelatedError(SomeType1())
 }

 do {
   try bar()
   try baz()
 }
 catch {
   // underlyingError will be injected
   rethrow .barOrBazRelatedError
 }

 ..
 if flag1 { throw .specialErrorOne(intValue) }
 ..
 if flag2 { throw .specialErrorTwo(stringValue) }
 ..
}

and then

func test() {
 do {
   ...
   try bat()
   ...
 }
 catch let e as BatError {
   switch e {
 case fooRelatedError(let some) : { print(some, e.underlyingError) }
 case barOrBazRelatedError : {
   

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 10:44 AM, Daniel Leping  wrote:
> 
> Mathew, I totally understand what you're talking about. And this is exactly 
> what I want to avoid.
> 
> If a library developer knows how to handle certain case (i.e. recover or 
> throw a special error) I'm ok with it. Though I'm not ok with completely 
> hiding errors from the underlying implementation. The library developer might 
> not handle EVERYTHING. I want to know what happened and where. Otherwise I 
> will have to explicitly debug his library to find out what the issue is. In 
> most cases I will not have time to wait developer fixing the library. I will 
> fix it myself and make PR. So I consider tranparent errors rather a benefit.
> 
> Hiding errors under library errors might have worked for proprietary 
> middleware. Today the world changed. It's open source now.

I agree with you that having *access* to the original error is important and 
that a library should not be expected to handle every case.  I hope I have not 
said anything that suggests otherwise.

What I’m suggesting is that if the error originated in a dependency that might 
change it should be wrapped in a stable abstraction that the users of the 
library can depend on.  The underlying error should still be available via a 
property or associated value exposed with an existential type.  If you expect 
users to catch errors thrown by a dependency that might change you have a 
fragile API contract in the area of errors.  

This can easily lead to unreliable software - when you change the dependency 
nothing alerts users to the fact that their error handling code is probably 
broken.  Even if the compiler told them it was broken, they now have the burden 
of learning about the errors that might come from your new dependency.  An API 
that could lead to this consequence is badly designed IMO.

I even suggested adding a new requirement to `Error` that includes a default 
implementation to standardize how we access wrapped errors, including the 
originating error underneath all wrapper layers.

> 
> My point is that I'm completely against typed throws. I'm sure there will be 
> a lot of developers who would try to use the feature to hide underlying 
> errors under their own. I can't consider it a good practice (reasons above). 
> This is how good intentions (caring about API and library users) can lead to 
> bad consequences. Sounds good in theory - works bad in practice (in real, non 
> ideal world).

What is your opinion about the problem I describe above?  Do you think this is 
unimportant?

> 
> On Tue, 28 Feb 2017 at 18:18 Matthew Johnson  > wrote:
> 
>> On Feb 28, 2017, at 5:09 AM, Daniel Leping > > wrote:
>> 
>> When you're going to present a user with specific error message you don't do 
>> it for all errors. First of all at the end of a call chain you will have a 
>> couple of dozens of possible errors and only 2 or 3 will do really matter 
>> for the end user. Would you list all the dozen in params? Probably not. 
>> You'll just catch what matters and put the rest to the log with a default 
>> "sorry" message.
>> 
>> What I mean is it's OK to be "unaware" why everything failed. What really 
>> does matter is what you can deal with and what you can't (the rest). "The 
>> rest" is usually a magnitude bigger, though.
>> 
>> I might agree, that listing some errors in the throws clause is useful 
>> feature for documentation (even so, I think the docs should be in the 
>> comments), but only and only if it can throw the listed errors + a bunch of 
>> other stuff. In Java it's resolved with a terrible solution of RuntimeError 
>> interface which I definitely suggest to avoid.
>> 
>> As a library developer I can say that even for a library it's a nightmare 
>> and libraries will start wrapping system errors with their own if the trend 
>> would become to list errors in middleware. My preference though is to have 
>> all errors transparently passed in both roles, being a library developer and 
>> a library user.
> 
> Whether to wrap or not is a judgment call.  
> 
> If the purpose of your library is to simply provide a cleaner experience for 
> a system API that is a stable dependency that will not change.  Users might 
> expect to have the errors from the system API forwarded directly and that 
> would be perfectly appropriate.  
> 
> In other cases you might have dependencies that are considered implementation 
> details.  In that case if you don’t wrap the errors you’re doing users a 
> disservice by giving them an unstable interface for error handling.  Changing 
> out your dependency that is supposed to be an implementation detail becomes a 
> breaking change.
> 
>> 
> 
>> On Tue, 28 Feb 2017 at 8:29 Karl Wagner > > wrote:
> 
>>> On 28 Feb 2017, at 02:43, Matthew Johnson via 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 6:47 AM, Vladimir.S  wrote:
> 
> On 28.02.2017 0:40, Matthew Johnson via swift-evolution wrote:
>> 
>>> On Feb 27, 2017, at 1:46 PM, David Waite via swift-evolution
>>> > wrote:
>>> 
>>> IMHO, there are two kinds of responses to errors - a specific response,
>>> and a general one. Only the calling code knows how it will deal with
>>> errors, so a “typed throws” is the function guessing possible calling
>>> code behavior.
>>> 
>>> The problem is, that gives four possible combinations - two where the
>>> function guesses correctly, and two where it doesn’t. The most damaging
>>> is when it suspects a caller doesn’t care about the error, when the
>>> caller actually does. This is unwanted wrapping.
>>> 
>>> To provide an example, imagine a library that parses JSON. It has several
>>> errors indicating JSON syntactic errors, and an “other” for representing
>>> errors on the input stream. It wraps the input stream errors so that it
>>> can provide a closed set of errors to the caller.
>>> 
>>> The caller is responsible for returning a data set. It doesn’t think that
>>> code calling ‘it” cares about JSON syntactic errors, merely that the
>>> object was not able to be restored. It returns its own wrapped error.
>>> 
>>> However, the original caller knows it is loading from disk. If the
>>> problem is due to an issue such as access permissions, It has to know
>>> implementation details of the API it called if it wishes to dive through
>>> the wrapped errors to find out if the problem was filesystem related.
>>> 
>>> Add more layers, and it can be very mysterious why a call failed. Java at
>>> least captures stack traces in this case to aid in technical support in
>>> diagnosing the error.
>>> 
>>> Wrapping exceptions also prevents an aggregate of errors from different
>>> subsystems being handled as a category, such as having a catch block
>>> handle RecoverableError generically
>> 
>> Fwiw, I think wrapping errors is something that people are sometimes going
>> to want to do regardless of whether they are typed or not.  Maybe the
>> solution is to better support wrapping errors by focusing on the problems
>> that wrapping causes.  For example, we could do something like this to make
> 
> Just to clarify, do you think about something like this? :
> (pseudocode, sorry for mistakes)

Is the question about the ability to create `fullErrorStackDescription`?  Yes, 
that would certainly be possible to implement in an extension.

I’m not sure why your examples use `rethrow` to rethrow the error instead of 
`throw`.  Was that a mistake?

I also want to reiterate that deciding when and how to wrap errors is something 
that requires careful thought and judgment.  The goal is to provide a stable 
and ergonomic way for callers to learn about and handle errors they are likely 
to care about.  There will often be an “everything else” case and that’s ok.  

As others have pointed out, the important thing is that it is easy to identify 
cases where I can *improve* UX by recovery or specific messaging to the user.  
The fact that there will usually be a default path is a given, but it’s best to 
avoid that path if possible.

> 
> func foo() throws {}
> 
> func bar() throws {}
> 
> enum BazError: Error { case baz1, case baz2 }
> func baz() throws(BazError) {..}
> 
> enum BatError: Error {
>  case fooRelatedError(SomeType1)
>  case barOrBazRelatedError
>  case specialErrorOne(Int)
>  case specialErrorTwo(String)
> }
> 
> func bat() throws(BatError) {
>  do {
>try foo()
>  }
>  catch {
>// underlyingError will be injected
>rethrow .fooRelatedError(SomeType1())
>  }
> 
>  do {
>try bar()
>try baz()
>  }
>  catch {
>// underlyingError will be injected
>rethrow .barOrBazRelatedError
>  }
> 
>  ..
>  if flag1 { throw .specialErrorOne(intValue) }
>  ..
>  if flag2 { throw .specialErrorTwo(stringValue) }
>  ..
> }
> 
> and then
> 
> func test() {
>  do {
>...
>try bat()
>...
>  }
>  catch let e as BatError {
>switch e {
>  case fooRelatedError(let some) : { print(some, e.underlyingError) }
>  case barOrBazRelatedError : {
>   print("something with bar or baz, so try this:..")
> 
>   if let bazError = e.underlyingError as BazError {
>   switch bazError {.}
>   } else {
>   // do something about "bar" error
>   }
>   }
>  case specialErrorOne(let i) : { print(i) }
>  case specialErrorTwo(let s) : { print(s) }
>}
> 
>log(e.fullErrorStackDescription) // BatError.description + 
> underlyingError.description + underlyingError.underlyingError.description etc
>  }...
> }
> 
> ?
> 
>> it easier to get at the original error:
>> 
>> protocol Error {
>>  // The error directly underlying this error.
>>  // Ideally the compiler would synthesize an implementation for enums
>> conforming to 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Daniel Leping via swift-evolution
Mathew, I totally understand what you're talking about. And this is exactly
what I want to avoid.

If a library developer knows how to handle certain case (i.e. recover or
throw a special error) I'm ok with it. Though I'm not ok with completely
hiding errors from the underlying implementation. The library developer
might not handle EVERYTHING. I want to know what happened and where.
Otherwise I will have to explicitly debug his library to find out what the
issue is. In most cases I will not have time to wait developer fixing the
library. I will fix it myself and make PR. So I consider tranparent errors
rather a benefit.

Hiding errors under library errors might have worked for proprietary
middleware. Today the world changed. It's open source now.

My point is that I'm completely against typed throws. I'm sure there will
be a lot of developers who would try to use the feature to hide underlying
errors under their own. I can't consider it a good practice (reasons
above). This is how good intentions (caring about API and library users)
can lead to bad consequences. Sounds good in theory - works bad in practice
(in real, non ideal world).

On Tue, 28 Feb 2017 at 18:18 Matthew Johnson  wrote:

>
> On Feb 28, 2017, at 5:09 AM, Daniel Leping 
> wrote:
>
> When you're going to present a user with specific error message you don't
> do it for all errors. First of all at the end of a call chain you will have
> a couple of dozens of possible errors and only 2 or 3 will do really matter
> for the end user. Would you list all the dozen in params? Probably not.
> You'll just catch what matters and put the rest to the log with a default
> "sorry" message.
>
> What I mean is it's OK to be "unaware" why everything failed. What really
> does matter is what you can deal with and what you can't (the rest). "The
> rest" is usually a magnitude bigger, though.
>
> I might agree, that listing some errors in the throws clause is useful
> feature for documentation (even so, I think the docs should be in the
> comments), but only and only if it can throw the listed errors + a bunch of
> other stuff. In Java it's resolved with a terrible solution of RuntimeError
> interface which I definitely suggest to avoid.
>
> As a library developer I can say that even for a library it's a nightmare
> and libraries will start wrapping system errors with their own if the trend
> would become to list errors in middleware. My preference though is to have
> all errors transparently passed in both roles, being a library developer
> and a library user.
>
>
> Whether to wrap or not is a judgment call.
>
> If the purpose of your library is to simply provide a cleaner experience
> for a system API that is a stable dependency that will not change.  Users
> might expect to have the errors from the system API forwarded directly and
> that would be perfectly appropriate.
>
> In other cases you might have dependencies that are considered
> implementation details.  In that case if you don’t wrap the errors you’re
> doing users a disservice by giving them an unstable interface for error
> handling.  Changing out your dependency that is supposed to be an
> implementation detail becomes a breaking change.
>
>
> On Tue, 28 Feb 2017 at 8:29 Karl Wagner  wrote:
>
> On 28 Feb 2017, at 02:43, Matthew Johnson via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>
>
> Sent from my iPad
>
> On Feb 27, 2017, at 7:19 PM, Daniel Leping 
> wrote:
>
> Well, as Dave pointed, you can very rarely recover from an error, which
> IMO is absolutely true.
>
> If your operation fails you don't really care unless you can recover. And
> you know your cases, which you can recover from (in reality one usually
> does it in optimization phase, though).
>
> What else do you need the type of error for at the very end of your call
> stack? In 90% you will tell the user "ah, sorry, something happened. Come
> back later" and log the error (if you haven't forgot it).
>
> In most cases the errors are not for recovering. They neither are to be
> presented to users. They are for developers to read the log/crash
> report/whatever else and analyze it. Most of the errors are for debugging
> purposes.
>
> I don't want to deal with cumbersome code the purpose of which is to just
> "obey the language rules". Unless I know how to recover I would rethrow it.
> Than catch at the top of the stack and log + show the user a nice "sorry"
> message without getting techy.
>
>
> In order to provide a helpful experience to end users an app needs to know
> about what might have caused the error and therefore what might (or might
> not) resolve it, allowing the operation to succeed on a subsequent
> attempt.  This can influence the strategy an app uses to avoid bothering
> the user if it might be resolvable without user intervention and can also
> influence the content of the message presented to users if that is
> 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 5:09 AM, Daniel Leping  wrote:
> 
> When you're going to present a user with specific error message you don't do 
> it for all errors. First of all at the end of a call chain you will have a 
> couple of dozens of possible errors and only 2 or 3 will do really matter for 
> the end user. Would you list all the dozen in params? Probably not. You'll 
> just catch what matters and put the rest to the log with a default "sorry" 
> message.
> 
> What I mean is it's OK to be "unaware" why everything failed. What really 
> does matter is what you can deal with and what you can't (the rest). "The 
> rest" is usually a magnitude bigger, though.
> 
> I might agree, that listing some errors in the throws clause is useful 
> feature for documentation (even so, I think the docs should be in the 
> comments), but only and only if it can throw the listed errors + a bunch of 
> other stuff. In Java it's resolved with a terrible solution of RuntimeError 
> interface which I definitely suggest to avoid.
> 
> As a library developer I can say that even for a library it's a nightmare and 
> libraries will start wrapping system errors with their own if the trend would 
> become to list errors in middleware. My preference though is to have all 
> errors transparently passed in both roles, being a library developer and a 
> library user.

Whether to wrap or not is a judgment call.  

If the purpose of your library is to simply provide a cleaner experience for a 
system API that is a stable dependency that will not change.  Users might 
expect to have the errors from the system API forwarded directly and that would 
be perfectly appropriate.  

In other cases you might have dependencies that are considered implementation 
details.  In that case if you don’t wrap the errors you’re doing users a 
disservice by giving them an unstable interface for error handling.  Changing 
out your dependency that is supposed to be an implementation detail becomes a 
breaking change.

> 
> On Tue, 28 Feb 2017 at 8:29 Karl Wagner  > wrote:
>> On 28 Feb 2017, at 02:43, Matthew Johnson via swift-evolution 
>> > wrote:
>> 
>> 
>> 
>> Sent from my iPad
>> 
>> On Feb 27, 2017, at 7:19 PM, Daniel Leping > > wrote:
>> 
>>> Well, as Dave pointed, you can very rarely recover from an error, which IMO 
>>> is absolutely true.
>>> 
>>> If your operation fails you don't really care unless you can recover. And 
>>> you know your cases, which you can recover from (in reality one usually 
>>> does it in optimization phase, though).
>>> 
>>> What else do you need the type of error for at the very end of your call 
>>> stack? In 90% you will tell the user "ah, sorry, something happened. Come 
>>> back later" and log the error (if you haven't forgot it).
>>> 
>>> In most cases the errors are not for recovering. They neither are to be 
>>> presented to users. They are for developers to read the log/crash 
>>> report/whatever else and analyze it. Most of the errors are for debugging 
>>> purposes.
>>> 
>>> I don't want to deal with cumbersome code the purpose of which is to just 
>>> "obey the language rules". Unless I know how to recover I would rethrow it. 
>>> Than catch at the top of the stack and log + show the user a nice "sorry" 
>>> message without getting techy.
>> 
>> In order to provide a helpful experience to end users an app needs to know 
>> about what might have caused the error and therefore what might (or might 
>> not) resolve it, allowing the operation to succeed on a subsequent attempt.  
>> This can influence the strategy an app uses to avoid bothering the user if 
>> it might be resolvable without user intervention and can also influence the 
>> content of the message presented to users if that is necessary.  
>> 
> 
> Errors are certainly useful, but the key distinction (IMO) is that callers 
> should never rely on an error happening or not. They are typically highly 
> implementation and hence version-specific. I think that means specific errors 
> shouldn’t be part of the function signature.
> 
> That doesn’t mean you can’t handle the error. A function that throws is one 
> that reserves the right to fail; maybe you can resolve the problem, but maybe 
> you can’t. That means your enclosing operation also needs to be prepared to 
> fail for reasons beyond _its_ control, and so on. At some point your entire 
> UI-level sub-operation (like opening the file, downloading the data from the 
> network) just fails, and you decide whether or not the user needs to be told 
> of that and how to do so.
> 
>> Indeed, many apps don't bother with this kind of detail and just treat all 
>> errors the same.  Personally, I find that to be an unfortunate state of 
>> affairs both as a user and as a developer.
>> 
>> Types can be useful in 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 28, 2017, at 12:29 AM, Karl Wagner  wrote:
> 
> 
>> On 28 Feb 2017, at 02:43, Matthew Johnson via swift-evolution 
>> > wrote:
>> 
>> 
>> 
>> Sent from my iPad
>> 
>> On Feb 27, 2017, at 7:19 PM, Daniel Leping > > wrote:
>> 
>>> Well, as Dave pointed, you can very rarely recover from an error, which IMO 
>>> is absolutely true.
>>> 
>>> If your operation fails you don't really care unless you can recover. And 
>>> you know your cases, which you can recover from (in reality one usually 
>>> does it in optimization phase, though).
>>> 
>>> What else do you need the type of error for at the very end of your call 
>>> stack? In 90% you will tell the user "ah, sorry, something happened. Come 
>>> back later" and log the error (if you haven't forgot it).
>>> 
>>> In most cases the errors are not for recovering. They neither are to be 
>>> presented to users. They are for developers to read the log/crash 
>>> report/whatever else and analyze it. Most of the errors are for debugging 
>>> purposes.
>>> 
>>> I don't want to deal with cumbersome code the purpose of which is to just 
>>> "obey the language rules". Unless I know how to recover I would rethrow it. 
>>> Than catch at the top of the stack and log + show the user a nice "sorry" 
>>> message without getting techy.
>> 
>> In order to provide a helpful experience to end users an app needs to know 
>> about what might have caused the error and therefore what might (or might 
>> not) resolve it, allowing the operation to succeed on a subsequent attempt.  
>> This can influence the strategy an app uses to avoid bothering the user if 
>> it might be resolvable without user intervention and can also influence the 
>> content of the message presented to users if that is necessary.  
>> 
> 
> Errors are certainly useful, but the key distinction (IMO) is that callers 
> should never rely on an error happening or not. They are typically highly 
> implementation and hence version-specific. I think that means specific errors 
> shouldn’t be part of the function signature.
> 
> That doesn’t mean you can’t handle the error. A function that throws is one 
> that reserves the right to fail; maybe you can resolve the problem, but maybe 
> you can’t. That means your enclosing operation also needs to be prepared to 
> fail for reasons beyond _its_ control, and so on. At some point your entire 
> UI-level sub-operation (like opening the file, downloading the data from the 
> network) just fails, and you decide whether or not the user needs to be told 
> of that and how to do so.

It is important that when this happens you need to be able to give the user as 
much useful information about what happened as possible.

> 
>> Indeed, many apps don't bother with this kind of detail and just treat all 
>> errors the same.  Personally, I find that to be an unfortunate state of 
>> affairs both as a user and as a developer.
>> 
>> Types can be useful in conveying this kind of information and abstracting 
>> low level details that are not helpful at higher levels in the stack.  Of 
>> course types are not the only way to convey this information.  But my 
>> experience is that untyped errors often result in libraries with poor 
>> documentation of error cases and not enough consideration of the error 
>> handling experience of users of the library in general.   That makes it very 
>> difficult to handle errors well.  I have experiences like this in several 
>> languages and on several platforms, including Apple's.  
> 
> I’m not sure that types are really the best abstraction for “the list of 
> errors that can be thrown by this function”. They’re fine for error 
> themselves, but not for per-function error-lists IMO. Most of our types are 
> way too rigid for this to be easy to live with.
> 
> If I understand what you’re saying, the core problem can be summarised as: "I 
> don’t know/can’t easily communicate which errors this function throws”. 
> Really, it is a documentation problem; it's too onerous to document every 
> individual throwing function, even with our very pretty markdown-like syntax. 
> I’m just not sure that the relatively rigid type-system is the solution.

If you have a better idea I would be thrilled to hear it, but documentation is 
not a good enough answer IMO.

> 
> Now, if we look at this from a documentation perspective: obviously, the 
> compiler _typically_ can't generate documentation for you, because it doesn’t 
> know what your types/functions are meant for. Error documentation, though, is 
> different: the compiler *can* often know the specific errors which get thrown 
> (or rethrown from a call to another function from whom it can get that 
> information); and even when it can’t know that, often it can at least know 
> the specific type of error. We could enhance the compiler libraries to track 
> that; I 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Matthew Johnson via swift-evolution

> On Feb 27, 2017, at 11:04 PM, David Waite  
> wrote:
> 
> 
>> On Feb 27, 2017, at 2:08 PM, Matthew Johnson > > wrote:
>> 
>>> 
>>> On Feb 27, 2017, at 1:46 PM, David Waite via swift-evolution 
>>> > wrote:
>>> 
>>> Add more layers, and it can be very mysterious why a call failed. Java at 
>>> least captures stack traces in this case to aid in technical support in 
>>> diagnosing the error.
>>> 
>>> Wrapping exceptions also prevents an aggregate of errors from different 
>>> subsystems being handled as a category, such as having a catch block handle 
>>> RecoverableError generically
>>> 
>>> An interesting solution that has emerged in Ruby to keep library authors 
>>> from wrapping exceptions is by decorating the existing exception. 
>>> Exceptions are caught via pattern matching (same as in Swift), so rather 
>>> than wrap an extension, they extend the error instance with a 
>>> library-specific module (e.g. swift protocol). So while the error may be a 
>>> IOError in ruby, you can still catch it via ‘rescue JSONError’
>> 
>> If I understand this correctly it sounds like introducing a library would 
>> create protocols to categorize errors and add retroactive conformances to 
>> these protocols for errors thrown by its dependencies?  That is an 
>> interesting approach.  But it requires knowing the concrete types of the 
>> possible errors all the way down the stack (you can’t add a conformance to 
>> an existential).  This seems very problematic to me, especially in a 
>> language where creating new error types is as easy as it is in Swift.
> 
> I believe it is something that even Objective C can’t do; extend a single 
> instance of a type to support a protocol.

Ok, I missed that it was a single instance, not all values with a given error 
type.

> 
> You can still do really interesting things with categories of errors, such as 
> extend existing types to support a protocol, and putting categorization logic 
> on third party errors yourself.
> 
> That breaks though if every library does
> 
> struct MyError: Error {
>let innerError: Error
> }
> 
> in order to have a typed throw. 

Did you see my suggestion for adding underlying and originating error 
properties to the `Error` protocol?  That would solve the problem of every 
library doing this themselves and make it much easier to identify what happened 
at the bottom of the stack when you really need to know that (without manually 
extracting wrapped errors all the way down).

> 
>> Error handling is messy, there’s no doubt about that.  I would like to have 
>> as many tools at my disposal as possible.  Error types is one of those tools.
> 
> I’m still trying to mentally put together the elevator pitch for typed 
> throws. How do I explain to new/junior developers and developers coming from 
> other languages when to do typed throws, and when not to?

IMO this is not that much different than any other aspect of API design.  This 
isn’t so much an elevator pitch as an education that error handling is messy 
and complicated and requires just as much thought in designing an API as the 
happy path does.  Nobody enjoys thinking about errors.  This is an opportunity 
to help teach junior developers to think more carefully about them.

Help junior developers learn to think about users of an API and think about 
dependencies and coupling.  When a user is calling this API how might they want 
to handle errors that might be thrown?  How can the error type help facilitate 
that?  On the other hand, when is an API exposing too much concrete type 
information to callers that creates unnecessary coupling?  When that is the 
case, how can we best remove enough type information to avoid that coupling 
(i.e. wrapping, exposing an existential, etc)? 

> 
> -DW
> 

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Vladimir.S via swift-evolution

On 28.02.2017 0:40, Matthew Johnson via swift-evolution wrote:



On Feb 27, 2017, at 1:46 PM, David Waite via swift-evolution
> wrote:

IMHO, there are two kinds of responses to errors - a specific response,
and a general one. Only the calling code knows how it will deal with
errors, so a “typed throws” is the function guessing possible calling
code behavior.

The problem is, that gives four possible combinations - two where the
function guesses correctly, and two where it doesn’t. The most damaging
is when it suspects a caller doesn’t care about the error, when the
caller actually does. This is unwanted wrapping.

To provide an example, imagine a library that parses JSON. It has several
errors indicating JSON syntactic errors, and an “other” for representing
errors on the input stream. It wraps the input stream errors so that it
can provide a closed set of errors to the caller.

The caller is responsible for returning a data set. It doesn’t think that
code calling ‘it” cares about JSON syntactic errors, merely that the
object was not able to be restored. It returns its own wrapped error.

However, the original caller knows it is loading from disk. If the
problem is due to an issue such as access permissions, It has to know
implementation details of the API it called if it wishes to dive through
the wrapped errors to find out if the problem was filesystem related.

Add more layers, and it can be very mysterious why a call failed. Java at
least captures stack traces in this case to aid in technical support in
diagnosing the error.

Wrapping exceptions also prevents an aggregate of errors from different
subsystems being handled as a category, such as having a catch block
handle RecoverableError generically


Fwiw, I think wrapping errors is something that people are sometimes going
to want to do regardless of whether they are typed or not.  Maybe the
solution is to better support wrapping errors by focusing on the problems
that wrapping causes.  For example, we could do something like this to make


Just to clarify, do you think about something like this? :
(pseudocode, sorry for mistakes)

func foo() throws {}

func bar() throws {}

enum BazError: Error { case baz1, case baz2 }
func baz() throws(BazError) {..}

enum BatError: Error {
  case fooRelatedError(SomeType1)
  case barOrBazRelatedError
  case specialErrorOne(Int)
  case specialErrorTwo(String)
}

func bat() throws(BatError) {
  do {
try foo()
  }
  catch {
// underlyingError will be injected
rethrow .fooRelatedError(SomeType1())
  }

  do {
try bar()
try baz()
  }
  catch {
// underlyingError will be injected
rethrow .barOrBazRelatedError
  }

  ..
  if flag1 { throw .specialErrorOne(intValue) }
  ..
  if flag2 { throw .specialErrorTwo(stringValue) }
  ..
}

and then

func test() {
  do {
...
try bat()
...
  }
  catch let e as BatError {
switch e {
  case fooRelatedError(let some) : { print(some, e.underlyingError) }
  case barOrBazRelatedError : {
print("something with bar or baz, so try this:..")

if let bazError = e.underlyingError as BazError {
switch bazError {.}
} else {
// do something about "bar" error
}
}
  case specialErrorOne(let i) : { print(i) }
  case specialErrorTwo(let s) : { print(s) }
}

log(e.fullErrorStackDescription) // BatError.description + 
underlyingError.description + underlyingError.underlyingError.description etc

  }...
}

?


it easier to get at the original error:

protocol Error {
  // The error directly underlying this error.
  // Ideally the compiler would synthesize an implementation for enums
conforming to `Error`
  // If `self` is a case that has an associate value which is or conforms
to `Error` that error would be returned, otherwise `nil` would be returned.
  var underlyingError: Error? { get }

  // The original error underlying *all* layers of wrapping.
  // If underlyingError is non-nil this is also non-nil.
  var originalError: Error { get }
}
extension Error {
var underlyingError: Error? {
  return nil
}
var originalError: Error {
  return underlyingError?.originalError ?? underlyingError ?? self
}
}

We could even provide syntactic sugar for catch sites that want to deal
with the original error rather than the wrapped error if that is an
important use case.



An interesting solution that has emerged in Ruby to keep library authors
from wrapping exceptions is by decorating the existing exception.
Exceptions are caught via pattern matching (same as in Swift), so rather
than wrap an extension, they extend the error instance with a
library-specific module (e.g. swift protocol). So while the error may be
a IOError in ruby, you can still catch it via ‘rescue JSONError’

Trying to specify the exact errors becomes even more destructive 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-28 Thread Daniel Leping via swift-evolution
When you're going to present a user with specific error message you don't
do it for all errors. First of all at the end of a call chain you will have
a couple of dozens of possible errors and only 2 or 3 will do really matter
for the end user. Would you list all the dozen in params? Probably not.
You'll just catch what matters and put the rest to the log with a default
"sorry" message.

What I mean is it's OK to be "unaware" why everything failed. What really
does matter is what you can deal with and what you can't (the rest). "The
rest" is usually a magnitude bigger, though.

I might agree, that listing some errors in the throws clause is useful
feature for documentation (even so, I think the docs should be in the
comments), but only and only if it can throw the listed errors + a bunch of
other stuff. In Java it's resolved with a terrible solution of RuntimeError
interface which I definitely suggest to avoid.

As a library developer I can say that even for a library it's a nightmare
and libraries will start wrapping system errors with their own if the trend
would become to list errors in middleware. My preference though is to have
all errors transparently passed in both roles, being a library developer
and a library user.

On Tue, 28 Feb 2017 at 8:29 Karl Wagner  wrote:

> On 28 Feb 2017, at 02:43, Matthew Johnson via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>
>
> Sent from my iPad
>
> On Feb 27, 2017, at 7:19 PM, Daniel Leping 
> wrote:
>
> Well, as Dave pointed, you can very rarely recover from an error, which
> IMO is absolutely true.
>
> If your operation fails you don't really care unless you can recover. And
> you know your cases, which you can recover from (in reality one usually
> does it in optimization phase, though).
>
> What else do you need the type of error for at the very end of your call
> stack? In 90% you will tell the user "ah, sorry, something happened. Come
> back later" and log the error (if you haven't forgot it).
>
> In most cases the errors are not for recovering. They neither are to be
> presented to users. They are for developers to read the log/crash
> report/whatever else and analyze it. Most of the errors are for debugging
> purposes.
>
> I don't want to deal with cumbersome code the purpose of which is to just
> "obey the language rules". Unless I know how to recover I would rethrow it.
> Than catch at the top of the stack and log + show the user a nice "sorry"
> message without getting techy.
>
>
> In order to provide a helpful experience to end users an app needs to know
> about what might have caused the error and therefore what might (or might
> not) resolve it, allowing the operation to succeed on a subsequent
> attempt.  This can influence the strategy an app uses to avoid bothering
> the user if it might be resolvable without user intervention and can also
> influence the content of the message presented to users if that is
> necessary.
>
>
> Errors are certainly useful, but the key distinction (IMO) is that callers
> should never rely on an error happening or not. They are typically highly
> implementation and hence version-specific. I think that means specific
> errors shouldn’t be part of the function signature.
>
> That doesn’t mean you can’t handle the error. A function that throws is
> one that reserves the right to fail; maybe you can resolve the problem, but
> maybe you can’t. That means your enclosing operation also needs to be
> prepared to fail for reasons beyond _its_ control, and so on. At some point
> your entire UI-level sub-operation (like opening the file, downloading the
> data from the network) just fails, and you decide whether or not the user
> needs to be told of that and how to do so.
>
> Indeed, many apps don't bother with this kind of detail and just treat all
> errors the same.  Personally, I find that to be an unfortunate state of
> affairs both as a user and as a developer.
>
> Types can be useful in conveying this kind of information and abstracting
> low level details that are not helpful at higher levels in the stack.  Of
> course types are not the only way to convey this information.  But my
> experience is that untyped errors often result in libraries with poor
> documentation of error cases and not enough consideration of the error
> handling experience of users of the library in general.   That makes it
> very difficult to handle errors well.  I have experiences like this in
> several languages and on several platforms, including Apple's.
>
>
> I’m not sure that types are really the best abstraction for “the list of
> errors that can be thrown by this function”. They’re fine for error
> themselves, but not for per-function error-lists IMO. Most of our types are
> way too rigid for this to be easy to live with.
>
> If I understand what you’re saying, the core problem can be summarised as:
> "I don’t know/can’t easily communicate which errors this function throws”.
> Really, 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Karl Wagner via swift-evolution

> On 28 Feb 2017, at 02:43, Matthew Johnson via swift-evolution 
>  wrote:
> 
> 
> 
> Sent from my iPad
> 
> On Feb 27, 2017, at 7:19 PM, Daniel Leping  > wrote:
> 
>> Well, as Dave pointed, you can very rarely recover from an error, which IMO 
>> is absolutely true.
>> 
>> If your operation fails you don't really care unless you can recover. And 
>> you know your cases, which you can recover from (in reality one usually does 
>> it in optimization phase, though).
>> 
>> What else do you need the type of error for at the very end of your call 
>> stack? In 90% you will tell the user "ah, sorry, something happened. Come 
>> back later" and log the error (if you haven't forgot it).
>> 
>> In most cases the errors are not for recovering. They neither are to be 
>> presented to users. They are for developers to read the log/crash 
>> report/whatever else and analyze it. Most of the errors are for debugging 
>> purposes.
>> 
>> I don't want to deal with cumbersome code the purpose of which is to just 
>> "obey the language rules". Unless I know how to recover I would rethrow it. 
>> Than catch at the top of the stack and log + show the user a nice "sorry" 
>> message without getting techy.
> 
> In order to provide a helpful experience to end users an app needs to know 
> about what might have caused the error and therefore what might (or might 
> not) resolve it, allowing the operation to succeed on a subsequent attempt.  
> This can influence the strategy an app uses to avoid bothering the user if it 
> might be resolvable without user intervention and can also influence the 
> content of the message presented to users if that is necessary.  
> 

Errors are certainly useful, but the key distinction (IMO) is that callers 
should never rely on an error happening or not. They are typically highly 
implementation and hence version-specific. I think that means specific errors 
shouldn’t be part of the function signature.

That doesn’t mean you can’t handle the error. A function that throws is one 
that reserves the right to fail; maybe you can resolve the problem, but maybe 
you can’t. That means your enclosing operation also needs to be prepared to 
fail for reasons beyond _its_ control, and so on. At some point your entire 
UI-level sub-operation (like opening the file, downloading the data from the 
network) just fails, and you decide whether or not the user needs to be told of 
that and how to do so.

> Indeed, many apps don't bother with this kind of detail and just treat all 
> errors the same.  Personally, I find that to be an unfortunate state of 
> affairs both as a user and as a developer.
> 
> Types can be useful in conveying this kind of information and abstracting low 
> level details that are not helpful at higher levels in the stack.  Of course 
> types are not the only way to convey this information.  But my experience is 
> that untyped errors often result in libraries with poor documentation of 
> error cases and not enough consideration of the error handling experience of 
> users of the library in general.   That makes it very difficult to handle 
> errors well.  I have experiences like this in several languages and on 
> several platforms, including Apple's.  

I’m not sure that types are really the best abstraction for “the list of errors 
that can be thrown by this function”. They’re fine for error themselves, but 
not for per-function error-lists IMO. Most of our types are way too rigid for 
this to be easy to live with.

If I understand what you’re saying, the core problem can be summarised as: "I 
don’t know/can’t easily communicate which errors this function throws”. Really, 
it is a documentation problem; it's too onerous to document every individual 
throwing function, even with our very pretty markdown-like syntax. I’m just not 
sure that the relatively rigid type-system is the solution.

Now, if we look at this from a documentation perspective: obviously, the 
compiler _typically_ can't generate documentation for you, because it doesn’t 
know what your types/functions are meant for. Error documentation, though, is 
different: the compiler *can* often know the specific errors which get thrown 
(or rethrown from a call to another function from whom it can get that 
information); and even when it can’t know that, often it can at least know the 
specific type of error. We could enhance the compiler libraries to track that; 
I spent half a day modifying the existing error-checker in Sema to prove the 
concept and it’s close.

Swift libraries don’t have header files, so third-parties will only ever see 
your documentation as generated by some tool which integrates the compiler. 
That means the compiler can “improve” the documentation when it is lacking. For 
yourself, if you care about explicitly writing out the errors which get thrown 
and you’re not working with an IDE, you could enable 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread David Waite via swift-evolution

> On Feb 27, 2017, at 2:08 PM, Matthew Johnson  wrote:
> 
>> 
>> On Feb 27, 2017, at 1:46 PM, David Waite via swift-evolution 
>> > wrote:
>> 
>> Add more layers, and it can be very mysterious why a call failed. Java at 
>> least captures stack traces in this case to aid in technical support in 
>> diagnosing the error.
>> 
>> Wrapping exceptions also prevents an aggregate of errors from different 
>> subsystems being handled as a category, such as having a catch block handle 
>> RecoverableError generically
>> 
>> An interesting solution that has emerged in Ruby to keep library authors 
>> from wrapping exceptions is by decorating the existing exception. Exceptions 
>> are caught via pattern matching (same as in Swift), so rather than wrap an 
>> extension, they extend the error instance with a library-specific module 
>> (e.g. swift protocol). So while the error may be a IOError in ruby, you can 
>> still catch it via ‘rescue JSONError’
> 
> If I understand this correctly it sounds like introducing a library would 
> create protocols to categorize errors and add retroactive conformances to 
> these protocols for errors thrown by its dependencies?  That is an 
> interesting approach.  But it requires knowing the concrete types of the 
> possible errors all the way down the stack (you can’t add a conformance to an 
> existential).  This seems very problematic to me, especially in a language 
> where creating new error types is as easy as it is in Swift.

I believe it is something that even Objective C can’t do; extend a single 
instance of a type to support a protocol.

You can still do really interesting things with categories of errors, such as 
extend existing types to support a protocol, and putting categorization logic 
on third party errors yourself.

That breaks though if every library does

struct MyError: Error {
   let innerError: Error
}

in order to have a typed throw. 

> Error handling is messy, there’s no doubt about that.  I would like to have 
> as many tools at my disposal as possible.  Error types is one of those tools.

I’m still trying to mentally put together the elevator pitch for typed throws. 
How do I explain to new/junior developers and developers coming from other 
languages when to do typed throws, and when not to?

-DW

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Matthew Johnson via swift-evolution


Sent from my iPad

> On Feb 27, 2017, at 8:21 PM, Dave Abrahams  wrote:
> 
> 
>> on Mon Feb 27 2017, Matthew Johnson  wrote:
>> 
>> If you don't believe typed errors will improve your code or if you
>> just don't want to deal with typed errors, just don't use them!
> 
> As I've tried to point out, it's not that simple.  You have to consider
> the complexity cost of adding a feature, whether people will be able to
> understand how to use it properly and when to avoid it, and what effect
> that use will have on the programming ecosystem.

I guess I believe the community is pretty wise about this kind of thing and 
have confidence in it to use this well especially if we are willing to refine 
the model as we learn from experience.  Who knows, maybe I'm too optimistic.

> 
> -- 
> -Dave

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Dave Abrahams via swift-evolution

on Mon Feb 27 2017, Matthew Johnson  wrote:

> If you don't believe typed errors will improve your code or if you
> just don't want to deal with typed errors, just don't use them!

As I've tried to point out, it's not that simple.  You have to consider
the complexity cost of adding a feature, whether people will be able to
understand how to use it properly and when to avoid it, and what effect
that use will have on the programming ecosystem.

-- 
-Dave
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Dave Abrahams via swift-evolution

on Mon Feb 27 2017, Daniel Leping  wrote:

> Well, as Dave pointed, you can very rarely recover from an error, which IMO
> is absolutely true.
>
> If your operation fails you don't really care unless you can recover. And
> you know your cases, which you can recover from (in reality one usually
> does it in optimization phase, though).
>
> What else do you need the type of error for at the very end of your call
> stack? In 90% you will tell the user "ah, sorry, something happened. Come
> back later" and log the error (if you haven't forgot it).
>
> In most cases the errors are not for recovering. They neither are to be
> presented to users. They are for developers to read the log/crash
> report/whatever else and analyze it. Most of the errors are for debugging
> purposes.

Disagreed, sorry.  When I said that recovery is rare, I meant that it is
generally done in a very few functions, not that it is rarely possible
at all.  If something goes wrong when editing a document, for example
full or network failure, you simply recover its previous state from the
undo stack.  Errors are *for* recoverable conditions.  If you're not
going to recover you might as well trap and save all the unwinding and
invariant preservation logic.

> I don't want to deal with cumbersome code the purpose of which is to just
> "obey the language rules". Unless I know how to recover I would rethrow it.
> Than catch at the top of the stack and log + show the user a nice "sorry"
> message without getting techy.
>
> On Tue, 28 Feb 2017 at 1:12 Matthew Johnson via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>>
>> > On Feb 27, 2017, at 5:01 PM, Dave Abrahams  wrote:
>> >
>> >
>> > on Mon Feb 27 2017, Matthew Johnson  wrote:
>> >
>> >>> On Feb 27, 2017, at 4:20 PM, Dave Abrahams 
>> wrote:
>> >>>
>> >>>
>> >>> I'm sorry, I don't see any substantive difference, based on what you've
>> >>> written here, between this feature and const.
>> >>
>> >> Let me give it one more shot and then I’ll drop it.  :)
>> >>
>> >> Const is viral because if an API does not declare its arguments const
>> >> it cannot be used by a caller who has a const argument.
>> >
>> > Unless the caller casts away const, thus erasing information that was
>> > previously encoded in the type system.
>>
>> Yes, of course.
>>
>> >
>> >> It is required in order to make an API as generally useful as
>> >> possible.
>> >>
>> >> Typed errors are not viral in this way because no callers are
>> >> prevented from calling an API regardless of whether it declares error
>> >> types or just throws Error like we have today.
>> >
>> > Unless the caller can recover (which is *very* rare) or it catches and
>> > rethrows one of the errors *it* declares, thus erasing information that
>> > was previously encoded in the type system.
>>
>> I view this as being fundamentally different than casting away const.
>> Casting away const says “I know better than the types”.
>>
>> Converting an error to a different type is extremely different.  It much
>> more similar to any other kind of value wrapper a library might create in
>> order to shield its users from being coupled to its implementation details
>> / dependencies.  This is not a way *around* the type system in the sense
>> that casting away const is.  It is a way of *using* the type system
>> (hopefully) to your advantage.
>>
>>
>>
>> >
>> >> Pressure to declare error types in your signature in order to make
>> >> your function as generally useful as possible does not exist.  Each
>> >> function is free to declare error types or not according to the
>> >> contract it wishes to expose.
>> >>
>> >> An argument can be made that community expectations might develop that
>> >> good APIs should declare error types and they could be considered
>> >> viral in this sense because any API that is simply declared `throws`
>> >> is dropping type information.  But I think this overstates the case.
>> >> The community appears to be very sensitive to the problems that can
>> >> arise when error types are too concrete, especially across module
>> >> boundaries.  I think we can learn to use the tool where it works well
>> >> and to avoid it where it causes problems.
>> >>
>> >>>
>> >>> --
>> >>> -Dave
>> >>
>> >
>> > --
>> > -Dave
>>
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>

-- 
-Dave
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Matthew Johnson via swift-evolution


Sent from my iPad

> On Feb 27, 2017, at 7:19 PM, Daniel Leping  wrote:
> 
> Well, as Dave pointed, you can very rarely recover from an error, which IMO 
> is absolutely true.
> 
> If your operation fails you don't really care unless you can recover. And you 
> know your cases, which you can recover from (in reality one usually does it 
> in optimization phase, though).
> 
> What else do you need the type of error for at the very end of your call 
> stack? In 90% you will tell the user "ah, sorry, something happened. Come 
> back later" and log the error (if you haven't forgot it).
> 
> In most cases the errors are not for recovering. They neither are to be 
> presented to users. They are for developers to read the log/crash 
> report/whatever else and analyze it. Most of the errors are for debugging 
> purposes.
> 
> I don't want to deal with cumbersome code the purpose of which is to just 
> "obey the language rules". Unless I know how to recover I would rethrow it. 
> Than catch at the top of the stack and log + show the user a nice "sorry" 
> message without getting techy.

In order to provide a helpful experience to end users an app needs to know 
about what might have caused the error and therefore what might (or might not) 
resolve it, allowing the operation to succeed on a subsequent attempt.  This 
can influence the strategy an app uses to avoid bothering the user if it might 
be resolvable without user intervention and can also influence the content of 
the message presented to users if that is necessary.  

Indeed, many apps don't bother with this kind of detail and just treat all 
errors the same.  Personally, I find that to be an unfortunate state of affairs 
both as a user and as a developer.

Types can be useful in conveying this kind of information and abstracting low 
level details that are not helpful at higher levels in the stack.  Of course 
types are not the only way to convey this information.  But my experience is 
that untyped errors often result in libraries with poor documentation of error 
cases and not enough consideration of the error handling experience of users of 
the library in general.   That makes it very difficult to handle errors well.  
I have experiences like this in several languages and on several platforms, 
including Apple's.  

Typed errors are certainly no panacea but I believe they can be an important 
tool.  Experience in other languages has shown that to be the case for many 
people writing many kinds of software.

If you don't believe typed errors will improve your code or if you just don't 
want to deal with typed errors, just don't use them!  You will be able to 
continue using untyped throws just as you do today.  You can even do this if 
you use libraries that throw typed errors.


> 
>> On Tue, 28 Feb 2017 at 1:12 Matthew Johnson via swift-evolution 
>>  wrote:
>> 
>> > On Feb 27, 2017, at 5:01 PM, Dave Abrahams  wrote:
>> >
>> >
>> > on Mon Feb 27 2017, Matthew Johnson  wrote:
>> >
>> >>> On Feb 27, 2017, at 4:20 PM, Dave Abrahams  wrote:
>> >>>
>> >>>
>> >>> I'm sorry, I don't see any substantive difference, based on what you've
>> >>> written here, between this feature and const.
>> >>
>> >> Let me give it one more shot and then I’ll drop it.  :)
>> >>
>> >> Const is viral because if an API does not declare its arguments const
>> >> it cannot be used by a caller who has a const argument.
>> >
>> > Unless the caller casts away const, thus erasing information that was
>> > previously encoded in the type system.
>> 
>> Yes, of course.
>> 
>> >
>> >> It is required in order to make an API as generally useful as
>> >> possible.
>> >>
>> >> Typed errors are not viral in this way because no callers are
>> >> prevented from calling an API regardless of whether it declares error
>> >> types or just throws Error like we have today.
>> >
>> > Unless the caller can recover (which is *very* rare) or it catches and
>> > rethrows one of the errors *it* declares, thus erasing information that
>> > was previously encoded in the type system.
>> 
>> I view this as being fundamentally different than casting away const.  
>> Casting away const says “I know better than the types”.
>> 
>> Converting an error to a different type is extremely different.  It much 
>> more similar to any other kind of value wrapper a library might create in 
>> order to shield its users from being coupled to its implementation details / 
>> dependencies.  This is not a way *around* the type system in the sense that 
>> casting away const is.  It is a way of *using* the type system (hopefully) 
>> to your advantage.
>> 
>> 
>> 
>> >
>> >> Pressure to declare error types in your signature in order to make
>> >> your function as generally useful as possible does not exist.  Each
>> >> function is free to declare error types or not according to the
>> >> contract it wishes to expose.
>> >>
>> >> An 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Daniel Leping via swift-evolution
Well, as Dave pointed, you can very rarely recover from an error, which IMO
is absolutely true.

If your operation fails you don't really care unless you can recover. And
you know your cases, which you can recover from (in reality one usually
does it in optimization phase, though).

What else do you need the type of error for at the very end of your call
stack? In 90% you will tell the user "ah, sorry, something happened. Come
back later" and log the error (if you haven't forgot it).

In most cases the errors are not for recovering. They neither are to be
presented to users. They are for developers to read the log/crash
report/whatever else and analyze it. Most of the errors are for debugging
purposes.

I don't want to deal with cumbersome code the purpose of which is to just
"obey the language rules". Unless I know how to recover I would rethrow it.
Than catch at the top of the stack and log + show the user a nice "sorry"
message without getting techy.

On Tue, 28 Feb 2017 at 1:12 Matthew Johnson via swift-evolution <
swift-evolution@swift.org> wrote:

>
> > On Feb 27, 2017, at 5:01 PM, Dave Abrahams  wrote:
> >
> >
> > on Mon Feb 27 2017, Matthew Johnson  wrote:
> >
> >>> On Feb 27, 2017, at 4:20 PM, Dave Abrahams 
> wrote:
> >>>
> >>>
> >>> I'm sorry, I don't see any substantive difference, based on what you've
> >>> written here, between this feature and const.
> >>
> >> Let me give it one more shot and then I’ll drop it.  :)
> >>
> >> Const is viral because if an API does not declare its arguments const
> >> it cannot be used by a caller who has a const argument.
> >
> > Unless the caller casts away const, thus erasing information that was
> > previously encoded in the type system.
>
> Yes, of course.
>
> >
> >> It is required in order to make an API as generally useful as
> >> possible.
> >>
> >> Typed errors are not viral in this way because no callers are
> >> prevented from calling an API regardless of whether it declares error
> >> types or just throws Error like we have today.
> >
> > Unless the caller can recover (which is *very* rare) or it catches and
> > rethrows one of the errors *it* declares, thus erasing information that
> > was previously encoded in the type system.
>
> I view this as being fundamentally different than casting away const.
> Casting away const says “I know better than the types”.
>
> Converting an error to a different type is extremely different.  It much
> more similar to any other kind of value wrapper a library might create in
> order to shield its users from being coupled to its implementation details
> / dependencies.  This is not a way *around* the type system in the sense
> that casting away const is.  It is a way of *using* the type system
> (hopefully) to your advantage.
>
>
>
> >
> >> Pressure to declare error types in your signature in order to make
> >> your function as generally useful as possible does not exist.  Each
> >> function is free to declare error types or not according to the
> >> contract it wishes to expose.
> >>
> >> An argument can be made that community expectations might develop that
> >> good APIs should declare error types and they could be considered
> >> viral in this sense because any API that is simply declared `throws`
> >> is dropping type information.  But I think this overstates the case.
> >> The community appears to be very sensitive to the problems that can
> >> arise when error types are too concrete, especially across module
> >> boundaries.  I think we can learn to use the tool where it works well
> >> and to avoid it where it causes problems.
> >>
> >>>
> >>> --
> >>> -Dave
> >>
> >
> > --
> > -Dave
>
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Matthew Johnson via swift-evolution

> On Feb 27, 2017, at 5:01 PM, Dave Abrahams  wrote:
> 
> 
> on Mon Feb 27 2017, Matthew Johnson  wrote:
> 
>>> On Feb 27, 2017, at 4:20 PM, Dave Abrahams  wrote:
>>> 
>>> 
>>> I'm sorry, I don't see any substantive difference, based on what you've
>>> written here, between this feature and const.
>> 
>> Let me give it one more shot and then I’ll drop it.  :)
>> 
>> Const is viral because if an API does not declare its arguments const
>> it cannot be used by a caller who has a const argument.  
> 
> Unless the caller casts away const, thus erasing information that was
> previously encoded in the type system.

Yes, of course.

> 
>> It is required in order to make an API as generally useful as
>> possible.
>> 
>> Typed errors are not viral in this way because no callers are
>> prevented from calling an API regardless of whether it declares error
>> types or just throws Error like we have today.  
> 
> Unless the caller can recover (which is *very* rare) or it catches and
> rethrows one of the errors *it* declares, thus erasing information that
> was previously encoded in the type system.

I view this as being fundamentally different than casting away const.  Casting 
away const says “I know better than the types”.

Converting an error to a different type is extremely different.  It much more 
similar to any other kind of value wrapper a library might create in order to 
shield its users from being coupled to its implementation details / 
dependencies.  This is not a way *around* the type system in the sense that 
casting away const is.  It is a way of *using* the type system (hopefully) to 
your advantage.



> 
>> Pressure to declare error types in your signature in order to make
>> your function as generally useful as possible does not exist.  Each
>> function is free to declare error types or not according to the
>> contract it wishes to expose.
>> 
>> An argument can be made that community expectations might develop that
>> good APIs should declare error types and they could be considered
>> viral in this sense because any API that is simply declared `throws`
>> is dropping type information.  But I think this overstates the case.
>> The community appears to be very sensitive to the problems that can
>> arise when error types are too concrete, especially across module
>> boundaries.  I think we can learn to use the tool where it works well
>> and to avoid it where it causes problems.
>> 
>>> 
>>> -- 
>>> -Dave
>> 
> 
> -- 
> -Dave

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Dave Abrahams via swift-evolution

on Mon Feb 27 2017, Matthew Johnson  wrote:

>> On Feb 27, 2017, at 4:20 PM, Dave Abrahams  wrote:
>> 
>> 
>> I'm sorry, I don't see any substantive difference, based on what you've
>> written here, between this feature and const.
>
> Let me give it one more shot and then I’ll drop it.  :)
>
> Const is viral because if an API does not declare its arguments const
> it cannot be used by a caller who has a const argument.  

Unless the caller casts away const, thus erasing information that was
previously encoded in the type system.

> It is required in order to make an API as generally useful as
> possible.
>
> Typed errors are not viral in this way because no callers are
> prevented from calling an API regardless of whether it declares error
> types or just throws Error like we have today.  

Unless the caller can recover (which is *very* rare) or it catches and
rethrows one of the errors *it* declares, thus erasing information that
was previously encoded in the type system.

> Pressure to declare error types in your signature in order to make
> your function as generally useful as possible does not exist.  Each
> function is free to declare error types or not according to the
> contract it wishes to expose.
>
> An argument can be made that community expectations might develop that
> good APIs should declare error types and they could be considered
> viral in this sense because any API that is simply declared `throws`
> is dropping type information.  But I think this overstates the case.
> The community appears to be very sensitive to the problems that can
> arise when error types are too concrete, especially across module
> boundaries.  I think we can learn to use the tool where it works well
> and to avoid it where it causes problems.
>
>> 
>> -- 
>> -Dave
>

-- 
-Dave
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Matthew Johnson via swift-evolution

> On Feb 27, 2017, at 4:44 PM, Zach Waldowski via swift-evolution 
>  wrote:
> 
> On Mon, Feb 27, 2017, at 04:05 PM, Daniel Leping via swift-evolution wrote:
>> David, IMHO, all you say is absolutely true and typed throws might work 
>> well, but in theoretic idealistic world. In reality though, you end having 
>> more exception types than data types, feature cost rises exponentially and 
>> the code becomes cluttered with all the wrapping.
>> 
>> I seriously don't understand why would one even think of this feature after 
>> it's proven a bad practice by Java community. Even Java based languages 
>> (i.e. Scala) have dropped this "feature".
> 
> I share Daniel's concerns 100%. The Swift community is so (rightfully) 
> concerned about stability right now, and all I can see is how this could tie 
> the hands of a future API author.
> 
> I'd love to have those fears mollified. If Swift can noticeably improve on 
> the typed throws model (through type checking, conversion, some ABI 
> guarantees for changing errors, etc.), like Swift has for a lot of features 
> other languages have tried and had trouble with, we should definitely move 
> forward with typed throws. If we can't, it does a lot to hurt what I think is 
> the great success of Swift's error-handling model.

Do you have a sense of the criteria you would use to determine whether the 
improvements are satisfactory to you or not?

> 
> Zach
> z...@waldowski.me 
> 
> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Matthew Johnson via swift-evolution

> On Feb 27, 2017, at 4:20 PM, Dave Abrahams  wrote:
> 
> 
> on Mon Feb 27 2017, Matthew Johnson  > wrote:
> 
>>> On Feb 27, 2017, at 10:48 AM, Dave Abrahams  wrote:
>>> 
>>> 
>>> on Mon Feb 27 2017, Matthew Johnson  wrote:
>>> 
>> 
> On Feb 27, 2017, at 12:32 AM, Dave Abrahams via swift-evolution 
>  wrote:
> 
> 
>> on Fri Feb 17 2017, Joe Groff  wrote:
>> 
>> Experience in other languages like Rust and Haskell that use
>> Result-based error propagation suggests that a single error type is
>> adequate, and beneficial in many ways. 
> 
> And experience in still others, like C++ and Java, suggests that
> using static types to restrict the kind of information a function can
> give you when an error occurs may actually be harmful.
 
 It seems to me that both can be true.  It is up to the author to
 determine which applies in a given use case.  I really don't think
 Swift should require those who understand where it is beneficial to
 use non-throwing functions that return a Result type if they wish to
 realize the benefits when they are relevant.
 
 My understanding of Chris's meaning of progressive disclosure is that
 the language should have powerful and general tools at its foundation
 that can be ignored until they are necessary for a particular problem.
>>> 
>>> For what problem is this feature necessary?
>> 
>> I was speaking about progressive disclosure generally.  You are right
>> that typed errors are not *necessary* for any problems.  Of course
>> that can be said about many language features.  Perhaps I should have
>> said "until they add value or clarity”.
>> 
>>> 
 In this case the powerful and general tool is typed errors.  The
 syntactic sugar allowing you to omit a type and therefore throw any
 error (typed as Error) allows users to ignore the more general tool
 when it isn't providing value.
 
 We should focus on educating users who wish to use this tool about the
 tradeoffs involved and how to think about what error type might be
 appropriate in different use cases rather than introduce an arbitrary
 (i.e. not technical) limitation prohibiting typed errors just because
 they can be badly used.  Understanding the tradeoffs is certainly not
 a beginner topic, but there are plenty of things in Swift that are not
 beginner topics.
>>> 
>>> In my experience, given an opportunity to encode something in the type
>>> system or categorize and annotate things using language constructs, most
>>> users will.  That's usually a great instinct, but not in this case, IMO.
>>> The hard problems of error recovery involve maintaining your program
>>> invariants, but this feature contributes nothing toward that end.  The
>>> one thing it *could* improve is the quality of error reporting to end
>>> users, but in practice that ends up devolving to dynamic lookups due to
>>> the need for localization.  So we truly gain very little from encoding
>>> this feature in the type system.
>> 
>> Error reporting and recovery are precisely where type errors can help.
>> I’ll give an example below.
>> 
>>> 
>>> This particular feature is viral in the same sense as C++ const, so I
>>> predict it will either see widespread use, which IMO would be harmful,
>>> or everyone will learn to avoid it.  Either way, it seems like a bad
>>> investment for the language.
>> 
>> I understand why you might make this comparison but I think there is
>> an important difference.
>> 
>> If I receive a const input I can only give that input to other things
>> that take a const.  It restricts my dependencies (what I can do with
>> the input), or conversely I have to abandon const or cast it away (!)
>> because my dependency isn’t declared as taking const.
>> 
>> With typed errors the situation is a little bit different.  My
>> signature is much more independent from that of my dependencies.
>> 
>> For example, I might have some dependencies which my users would
>> prefer to not be tightly coupled with that happen to throw errors.
>> For example, maybe I depend on an IO library and a parsing library.
>> These dependencies are subject to change in the future.  Rather than
>> let the errors propagate directly I wrap them in `enum MyLIbraryError
>> { case IOError(Error); case ParsingError(Error) }`.
>> 
>> This could provide a meaningful abstraction / grouping that helps the
>> client know what might or might not solve the problem.  
> 
> And it might not.  It might turn out that something you have to call
> throws an error that doesn't fit into either case.  Remember, the things
> you call may be closures or methods supplied to you by your clients.
> Then you have to resort to the equivalent of casting away const.
> 
>> It might be worth exposing the error type to callers giving them 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Zach Waldowski via swift-evolution
On Mon, Feb 27, 2017, at 04:05 PM, Daniel Leping via swift-evolution wrote:
> David, IMHO, all you say is absolutely true and typed throws might
> work well, but in theoretic idealistic world. In reality though, you
> end having more exception types than data types, feature cost rises
> exponentially and the code becomes cluttered with all the wrapping.
> 

> I seriously don't understand why would one even think of this feature
> after it's proven a bad practice by Java community. Even Java based
> languages (i.e. Scala) have dropped this "feature".


I share Daniel's concerns 100%. The Swift community is so (rightfully)
concerned about stability right now, and all I can see is how this could
tie the hands of a future API author.


I'd love to have those fears mollified. If Swift can noticeably improve
on the typed throws model (through type checking, conversion, some ABI
guarantees for changing errors, etc.), like Swift has for a lot of
features other languages have tried and had trouble with, we should
definitely move forward with typed throws. If we can't, it does a lot to
hurt what I think is the great success of Swift's error-handling model.


Zach

z...@waldowski.me




___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Matthew Johnson via swift-evolution

> On Feb 27, 2017, at 3:34 PM, Joe Groff via swift-evolution 
>  wrote:
> 
> 
>> On Feb 27, 2017, at 4:19 AM, Daniel Leping via swift-evolution 
>> > wrote:
>> 
>> 
>> On Mon, 27 Feb 2017 at 8:44 Dave Abrahams via swift-evolution 
>> > wrote:
>> 
>> on Fri Feb 17 2017, Joe Groff > > wrote:
>> 
>> > Experience in other languages like Rust and Haskell that use
>> > Result-based error propagation suggests that a single error type is
>> > adequate, and beneficial in many ways.
>> 
>> 
>> And experience in still others, like C++ and Java, suggests that
>> using static types to restrict the kind of information a function can
>> give you when an error occurs may actually be harmful.
>> +1 here. It becomes wrapping over wrapping over wrapping. Try doing a big 
>> app in Java (i.e. some kind of layered server) and you'll understand 
>> everything. Ones who tried and still want it - well, there are different 
>> tastes out there.
> 
> OTOH, people *don't* seem to have these problems with Rust and functional 
> languages with value-oriented error handling. This could be partly because 
> there's a greater focus on fully-closed systems in those communities where 
> resilience isn't a concern, and you can usually evolve all your use sites if 
> you need to break an API, whereas C++ and Java projects are more likely to 
> incorporate black-box components from multiple sources. Having affordances 
> for unwinding with a well-typed error *within* a component seems like a 
> generally useful thing; Haskell has do notation and Rust tosses macros at the 
> problem to hide the propagation boilerplate, after all.

+1.  There are places where typed errors are very useful and places where they 
can cause problems (as with many features a programming language can have).

> 
> -Joe
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Matthew Johnson via swift-evolution

> On Feb 27, 2017, at 1:46 PM, David Waite via swift-evolution 
>  wrote:
> 
> IMHO, there are two kinds of responses to errors - a specific response, and a 
> general one. Only the calling code knows how it will deal with errors, so a 
> “typed throws” is the function guessing possible calling code behavior.
> 
> The problem is, that gives four possible combinations - two where the 
> function guesses correctly, and two where it doesn’t. The most damaging is 
> when it suspects a caller doesn’t care about the error, when the caller 
> actually does. This is unwanted wrapping.
> 
> To provide an example, imagine a library that parses JSON. It has several 
> errors indicating JSON syntactic errors, and an “other” for representing 
> errors on the input stream. It wraps the input stream errors so that it can 
> provide a closed set of errors to the caller.
> 
> The caller is responsible for returning a data set. It doesn’t think that 
> code calling ‘it” cares about JSON syntactic errors, merely that the object 
> was not able to be restored. It returns its own wrapped error.
> 
> However, the original caller knows it is loading from disk. If the problem is 
> due to an issue such as access permissions, It has to know implementation 
> details of the API it called if it wishes to dive through the wrapped errors 
> to find out if the problem was filesystem related.
> 
> Add more layers, and it can be very mysterious why a call failed. Java at 
> least captures stack traces in this case to aid in technical support in 
> diagnosing the error.
> 
> Wrapping exceptions also prevents an aggregate of errors from different 
> subsystems being handled as a category, such as having a catch block handle 
> RecoverableError generically

Fwiw, I think wrapping errors is something that people are sometimes going to 
want to do regardless of whether they are typed or not.  Maybe the solution is 
to better support wrapping errors by focusing on the problems that wrapping 
causes.  For example, we could do something like this to make it easier to get 
at the original error:

protocol Error {
  // The error directly underlying this error.
  // Ideally the compiler would synthesize an implementation for enums 
conforming to `Error`
  // If `self` is a case that has an associate value which is or conforms to 
`Error` that error would be returned, otherwise `nil` would be returned.
  var underlyingError: Error? { get }

  // The original error underlying *all* layers of wrapping.
  // If underlyingError is non-nil this is also non-nil.
  var originalError: Error { get }
}
extension Error {
var underlyingError: Error? { 
  return nil
}
var originalError: Error {
  return underlyingError?.originalError ?? underlyingError ?? self
}
}

We could even provide syntactic sugar for catch sites that want to deal with 
the original error rather than the wrapped error if that is an important use 
case.

> 
> An interesting solution that has emerged in Ruby to keep library authors from 
> wrapping exceptions is by decorating the existing exception. Exceptions are 
> caught via pattern matching (same as in Swift), so rather than wrap an 
> extension, they extend the error instance with a library-specific module 
> (e.g. swift protocol). So while the error may be a IOError in ruby, you can 
> still catch it via ‘rescue JSONError’
> 
> Trying to specify the exact errors becomes even more destructive with 
> protocols and closures, where the person defining the interface knows neither 
> which errors the implementor of the call will throw, nor necessarily if the 
> caller will want to implement specific behavior on those errors. This in my 
> personal Java coding experience almost always leads to wrapping in some 
> protocol-specific Exception type which exposes minimal information to the 
> caller, or exposing your errors in some unrelated type like IOException which 
> was declared based on the author’s experience of possible exceptions.
> 
> -DW
>  
>> On Feb 27, 2017, at 5:19 AM, Daniel Leping via swift-evolution 
>> > wrote:
>> 
>> 
>> On Mon, 27 Feb 2017 at 8:44 Dave Abrahams via swift-evolution 
>> > wrote:
>> 
>> on Fri Feb 17 2017, Joe Groff > > wrote:
>> 
>> > Experience in other languages like Rust and Haskell that use
>> > Result-based error propagation suggests that a single error type is
>> > adequate, and beneficial in many ways.
>> 
>> 
>> And experience in still others, like C++ and Java, suggests that
>> using static types to restrict the kind of information a function can
>> give you when an error occurs may actually be harmful.
>> +1 here. It becomes wrapping over wrapping over wrapping. Try doing a big 
>> app in Java (i.e. some kind of layered server) and you'll understand 
>> everything. 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Joe Groff via swift-evolution

> On Feb 27, 2017, at 4:19 AM, Daniel Leping via swift-evolution 
>  wrote:
> 
> 
> On Mon, 27 Feb 2017 at 8:44 Dave Abrahams via swift-evolution 
> > wrote:
> 
> on Fri Feb 17 2017, Joe Groff  > wrote:
> 
> > Experience in other languages like Rust and Haskell that use
> > Result-based error propagation suggests that a single error type is
> > adequate, and beneficial in many ways.
> 
> 
> And experience in still others, like C++ and Java, suggests that
> using static types to restrict the kind of information a function can
> give you when an error occurs may actually be harmful.
> +1 here. It becomes wrapping over wrapping over wrapping. Try doing a big app 
> in Java (i.e. some kind of layered server) and you'll understand everything. 
> Ones who tried and still want it - well, there are different tastes out there.

OTOH, people *don't* seem to have these problems with Rust and functional 
languages with value-oriented error handling. This could be partly because 
there's a greater focus on fully-closed systems in those communities where 
resilience isn't a concern, and you can usually evolve all your use sites if 
you need to break an API, whereas C++ and Java projects are more likely to 
incorporate black-box components from multiple sources. Having affordances for 
unwinding with a well-typed error *within* a component seems like a generally 
useful thing; Haskell has do notation and Rust tosses macros at the problem to 
hide the propagation boilerplate, after all.

-Joe
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Daniel Leping via swift-evolution
David, IMHO, all you say is absolutely true and typed throws might work
well, but in theoretic idealistic world. In reality though, you end having
more exception types than data types, feature cost rises exponentially and
the code becomes cluttered with all the wrapping.

I seriously don't understand why would one even think of this feature after
it's proven a bad practice by Java community. Even Java based languages
(i.e. Scala) have dropped this "feature".

On Mon, 27 Feb 2017 at 21:46 David Waite 
wrote:

> IMHO, there are two kinds of responses to errors - a specific response,
> and a general one. Only the calling code knows how it will deal with
> errors, so a “typed throws” is the function guessing possible calling code
> behavior.
>
> The problem is, that gives four possible combinations - two where the
> function guesses correctly, and two where it doesn’t. The most damaging is
> when it suspects a caller doesn’t care about the error, when the caller
> actually does. This is unwanted wrapping.
>
> To provide an example, imagine a library that parses JSON. It has several
> errors indicating JSON syntactic errors, and an “other” for representing
> errors on the input stream. It wraps the input stream errors so that it can
> provide a closed set of errors to the caller.
>
> The caller is responsible for returning a data set. It doesn’t think that
> code calling ‘it” cares about JSON syntactic errors, merely that the object
> was not able to be restored. It returns its own wrapped error.
>
> However, the original caller knows it is loading from disk. If the problem
> is due to an issue such as access permissions, It has to know
> implementation details of the API it called if it wishes to dive through
> the wrapped errors to find out if the problem was filesystem related.
>
> Add more layers, and it can be very mysterious why a call failed. Java at
> least captures stack traces in this case to aid in technical support in
> diagnosing the error.
>
> Wrapping exceptions also prevents an aggregate of errors from different
> subsystems being handled as a category, such as having a catch block handle
> RecoverableError generically
>
> An interesting solution that has emerged in Ruby to keep library authors
> from wrapping exceptions is by decorating the existing exception.
> Exceptions are caught via pattern matching (same as in Swift), so rather
> than wrap an extension, they extend the error instance with a
> library-specific module (e.g. swift protocol). So while the error may be a
> IOError in ruby, you can still catch it via ‘rescue JSONError’
>
> Trying to specify the exact errors becomes even more destructive with
> protocols and closures, where the person defining the interface knows
> neither which errors the implementor of the call will throw, nor
> necessarily if the caller will want to implement specific behavior on those
> errors. This in my personal Java coding experience almost always leads to
> wrapping in some protocol-specific Exception type which exposes minimal
> information to the caller, or exposing your errors in some unrelated type
> like IOException which was declared based on the author’s experience of
> possible exceptions.
>
> -DW
>
>
> On Feb 27, 2017, at 5:19 AM, Daniel Leping via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>
> On Mon, 27 Feb 2017 at 8:44 Dave Abrahams via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>
> on Fri Feb 17 2017, Joe Groff  wrote:
>
> > Experience in other languages like Rust and Haskell that use
> > Result-based error propagation suggests that a single error type is
> > adequate, and beneficial in many ways.
>
>
>
> And experience in still others, like C++ and Java, suggests that
> using static types to restrict the kind of information a function can
> give you when an error occurs may actually be harmful.
>
> +1 here. It becomes wrapping over wrapping over wrapping. Try doing a big
> app in Java (i.e. some kind of layered server) and you'll understand
> everything. Ones who tried and still want it - well, there are different
> tastes out there.
>
>
>
> --
> -Dave
>
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Matthew Johnson via swift-evolution

> On Feb 27, 2017, at 1:46 PM, David Waite via swift-evolution 
>  wrote:
> 
> IMHO, there are two kinds of responses to errors - a specific response, and a 
> general one. Only the calling code knows how it will deal with errors, so a 
> “typed throws” is the function guessing possible calling code behavior.
> 
> The problem is, that gives four possible combinations - two where the 
> function guesses correctly, and two where it doesn’t. The most damaging is 
> when it suspects a caller doesn’t care about the error, when the caller 
> actually does. This is unwanted wrapping.
> 
> To provide an example, imagine a library that parses JSON. It has several 
> errors indicating JSON syntactic errors, and an “other” for representing 
> errors on the input stream. It wraps the input stream errors so that it can 
> provide a closed set of errors to the caller.
> 
> The caller is responsible for returning a data set. It doesn’t think that 
> code calling ‘it” cares about JSON syntactic errors, merely that the object 
> was not able to be restored. It returns its own wrapped error.
> 
> However, the original caller knows it is loading from disk. If the problem is 
> due to an issue such as access permissions, It has to know implementation 
> details of the API it called if it wishes to dive through the wrapped errors 
> to find out if the problem was filesystem related.

On the other hand, without wrapping the caller still needs to know the 
implementation details of the API in order to know what errors its dependencies 
might throw and the burden of grouping errors into higher-level categories is 
punted to the caller.  Some callers might appreciate the directness and 
specificity of the original error but many others might appreciate the higher 
level grouping.

This is ultimately a question of API design where there is no one right answer.

> 
> Add more layers, and it can be very mysterious why a call failed. Java at 
> least captures stack traces in this case to aid in technical support in 
> diagnosing the error.
> 
> Wrapping exceptions also prevents an aggregate of errors from different 
> subsystems being handled as a category, such as having a catch block handle 
> RecoverableError generically
> 
> An interesting solution that has emerged in Ruby to keep library authors from 
> wrapping exceptions is by decorating the existing exception. Exceptions are 
> caught via pattern matching (same as in Swift), so rather than wrap an 
> extension, they extend the error instance with a library-specific module 
> (e.g. swift protocol). So while the error may be a IOError in ruby, you can 
> still catch it via ‘rescue JSONError’

If I understand this correctly it sounds like introducing a library would 
create protocols to categorize errors and add retroactive conformances to these 
protocols for errors thrown by its dependencies?  That is an interesting 
approach.  But it requires knowing the concrete types of the possible errors 
all the way down the stack (you can’t add a conformance to an existential).  
This seems very problematic to me, especially in a language where creating new 
error types is as easy as it is in Swift.

Error handling is messy, there’s no doubt about that.  I would like to have as 
many tools at my disposal as possible.  Error types is one of those tools.

> 
> Trying to specify the exact errors becomes even more destructive with 
> protocols and closures, where the person defining the interface knows neither 
> which errors the implementor of the call will throw, nor necessarily if the 
> caller will want to implement specific behavior on those errors. This in my 
> personal Java coding experience almost always leads to wrapping in some 
> protocol-specific Exception type which exposes minimal information to the 
> caller, or exposing your errors in some unrelated type like IOException which 
> was declared based on the author’s experience of possible exceptions.
> 
> -DW
>  
>> On Feb 27, 2017, at 5:19 AM, Daniel Leping via swift-evolution 
>> > wrote:
>> 
>> 
>> On Mon, 27 Feb 2017 at 8:44 Dave Abrahams via swift-evolution 
>> > wrote:
>> 
>> on Fri Feb 17 2017, Joe Groff > > wrote:
>> 
>> > Experience in other languages like Rust and Haskell that use
>> > Result-based error propagation suggests that a single error type is
>> > adequate, and beneficial in many ways.
>> 
>> 
>> And experience in still others, like C++ and Java, suggests that
>> using static types to restrict the kind of information a function can
>> give you when an error occurs may actually be harmful.
>> +1 here. It becomes wrapping over wrapping over wrapping. Try doing a big 
>> app in Java (i.e. some kind of layered server) and you'll understand 
>> everything. Ones who tried and still want it - well, there 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread David Waite via swift-evolution
IMHO, there are two kinds of responses to errors - a specific response, and a 
general one. Only the calling code knows how it will deal with errors, so a 
“typed throws” is the function guessing possible calling code behavior.

The problem is, that gives four possible combinations - two where the function 
guesses correctly, and two where it doesn’t. The most damaging is when it 
suspects a caller doesn’t care about the error, when the caller actually does. 
This is unwanted wrapping.

To provide an example, imagine a library that parses JSON. It has several 
errors indicating JSON syntactic errors, and an “other” for representing errors 
on the input stream. It wraps the input stream errors so that it can provide a 
closed set of errors to the caller.

The caller is responsible for returning a data set. It doesn’t think that code 
calling ‘it” cares about JSON syntactic errors, merely that the object was not 
able to be restored. It returns its own wrapped error.

However, the original caller knows it is loading from disk. If the problem is 
due to an issue such as access permissions, It has to know implementation 
details of the API it called if it wishes to dive through the wrapped errors to 
find out if the problem was filesystem related.

Add more layers, and it can be very mysterious why a call failed. Java at least 
captures stack traces in this case to aid in technical support in diagnosing 
the error.

Wrapping exceptions also prevents an aggregate of errors from different 
subsystems being handled as a category, such as having a catch block handle 
RecoverableError generically

An interesting solution that has emerged in Ruby to keep library authors from 
wrapping exceptions is by decorating the existing exception. Exceptions are 
caught via pattern matching (same as in Swift), so rather than wrap an 
extension, they extend the error instance with a library-specific module (e.g. 
swift protocol). So while the error may be a IOError in ruby, you can still 
catch it via ‘rescue JSONError’

Trying to specify the exact errors becomes even more destructive with protocols 
and closures, where the person defining the interface knows neither which 
errors the implementor of the call will throw, nor necessarily if the caller 
will want to implement specific behavior on those errors. This in my personal 
Java coding experience almost always leads to wrapping in some 
protocol-specific Exception type which exposes minimal information to the 
caller, or exposing your errors in some unrelated type like IOException which 
was declared based on the author’s experience of possible exceptions.

-DW
 
> On Feb 27, 2017, at 5:19 AM, Daniel Leping via swift-evolution 
>  wrote:
> 
> 
> On Mon, 27 Feb 2017 at 8:44 Dave Abrahams via swift-evolution 
> > wrote:
> 
> on Fri Feb 17 2017, Joe Groff  > wrote:
> 
> > Experience in other languages like Rust and Haskell that use
> > Result-based error propagation suggests that a single error type is
> > adequate, and beneficial in many ways.
> 
> 
> And experience in still others, like C++ and Java, suggests that
> using static types to restrict the kind of information a function can
> give you when an error occurs may actually be harmful.
> +1 here. It becomes wrapping over wrapping over wrapping. Try doing a big app 
> in Java (i.e. some kind of layered server) and you'll understand everything. 
> Ones who tried and still want it - well, there are different tastes out there.
> 
> 
> --
> -Dave
> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org 
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-27 Thread Daniel Leping via swift-evolution
On Mon, 27 Feb 2017 at 8:44 Dave Abrahams via swift-evolution <
swift-evolution@swift.org> wrote:

>
> on Fri Feb 17 2017, Joe Groff  wrote:
>
> > Experience in other languages like Rust and Haskell that use
> > Result-based error propagation suggests that a single error type is
> > adequate, and beneficial in many ways.


>
> And experience in still others, like C++ and Java, suggests that
> using static types to restrict the kind of information a function can
> give you when an error occurs may actually be harmful.

+1 here. It becomes wrapping over wrapping over wrapping. Try doing a big
app in Java (i.e. some kind of layered server) and you'll understand
everything. Ones who tried and still want it - well, there are different
tastes out there.

>
>
> --
> -Dave
>
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-26 Thread Dave Abrahams via swift-evolution

on Fri Feb 17 2017, Joe Groff  wrote:

> Experience in other languages like Rust and Haskell that use
> Result-based error propagation suggests that a single error type is
> adequate, and beneficial in many ways. 

And experience in still others, like C++ and Java, suggests that
using static types to restrict the kind of information a function can
give you when an error occurs may actually be harmful.

-- 
-Dave

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-24 Thread Russ Bishop via swift-evolution

> On Feb 21, 2017, at 8:18 AM, Joe Groff  wrote:
>>> 
>> 
>> Yeah but Rust has a macro system so error-chain makes re-wrapping trivial. A 
>> simple `chain_err(|| “my new error message”)` is enough to define and 
>> “throw” a new error that wraps the underlying error.
> 
> That was my point—they recognized error chaining/layering as a common 
> phonemenon and provided affordances for it as part of their design. I was 
> saying we should do the same if we investigate typed errors seriously. (Not 
> sure why you mentioned macros—chain_err is just a regular method, and could 
> be defined as an extension method on Error in swift today.)
> 
> -Joe

Sorry, I meant error_chain! handles the boilerplate. At the end of the day 
something has to create the new error types.

I agree with you on the chaining; if we really do want to go down this road we 
need an elegant solution. Seeing people talk about getting rid of rethrows 
makes me think this isn’t yet a serious discussion.

Russ___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-24 Thread Matthew Johnson via swift-evolution

> On Feb 24, 2017, at 11:00 AM, Joe Groff via swift-evolution 
>  wrote:
> 
> 
>> On Feb 23, 2017, at 10:36 PM, Russ Bishop > > wrote:
>> 
>> 
>>> On Feb 21, 2017, at 8:18 AM, Joe Groff >> > wrote:
> 
 
 Yeah but Rust has a macro system so error-chain makes re-wrapping trivial. 
 A simple `chain_err(|| “my new error message”)` is enough to define and 
 “throw” a new error that wraps the underlying error.
>>> 
>>> That was my point—they recognized error chaining/layering as a common 
>>> phonemenon and provided affordances for it as part of their design. I was 
>>> saying we should do the same if we investigate typed errors seriously. (Not 
>>> sure why you mentioned macros—chain_err is just a regular method, and could 
>>> be defined as an extension method on Error in swift today.)
>>> 
>>> -Joe
>> 
>> Sorry, I meant error_chain! handles the boilerplate. At the end of the day 
>> something has to create the new error types.
>> 
>> I agree with you on the chaining; if we really do want to go down this road 
>> we need an elegant solution. Seeing people talk about getting rid of 
>> rethrows makes me think this isn’t yet a serious discussion.

Is it just the syntactic sugar you would miss or is there more to it?  Did you 
see the analysis I sent out showing a case rethrows can’t handle and a problem 
the current proposal has in handling typed rethrows?

> 
> If `rethrows` can be turned into sugar for regular type parametricity, I 
> think that'd be a win over our current model. The sugar is probably still 
> useful since ` (() throws E -> ()) throws SomethingDerivedFrom -> ()` 
> is common enough; whether SomethingDerivedFrom is E, Error, or 
> MyModulesErrorWrapper would have to be discussed.

I don’t mind keeping rethrows as syntactic sugar but I would like to have a 
solution that can handle all cases including the one it isn’t able to handle.

I almost included something like this in the analysis I sent out.  I thought 
maybe injecting implicit generic parameters would be a little bit too magical.  
It would be super convenient though!



> 
> -Joe
> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-24 Thread Karl Wagner via swift-evolution

> On 23 Feb 2017, at 17:19, Joe Groff  wrote:
> 
>> 
>> On Feb 23, 2017, at 12:06 AM, Karl Wagner  wrote:
>> 
>> 
>>> On 22 Feb 2017, at 21:13, Joe Groff  wrote:
>>> 
 
 On Feb 21, 2017, at 8:50 PM, Chris Lattner via swift-evolution 
  wrote:
 
 On Feb 20, 2017, at 11:12 PM, John McCall  wrote:
>> As you know, I still think that adding typed throws is the right thing 
>> to do.  I understand your concern about “the feature could be misused” 
>> but the same thing is true about many other language features.
> 
> That's fair, but I do think there's an important difference here.  The 
> way I see it, typed-throws is really something of an expert feature, not 
> because it's at all difficult to use, but the reverse: because it's easy 
> to use without really thinking about the consequences.  (And the benefits 
> are pretty subtle, too.)  I'm not saying that we should design it to be 
> hard to use, but I think maybe it shouldn't immediately suggest itself, 
> and it especially shouldn't come across as just a more specific version 
> of throws.
 
 Yeah, I agree that it will be appealing to people who don’t know better, 
 but here’s the thing: the (almost certain) Swift design will prevent the 
 bad thing from happening in practice.
 
 Consider the barriers Swift already puts in place to prevent the bad thing 
 (declaring an inappropriately narrow explicitly-specified throw signature) 
 from happening:
 
 1) First of all, you need to declare a public API.  If it isn’t public, 
 then there is no concern at all, you can evolve the implementation and 
 clients together.
 
 2) The Second problem depends on the number of errors it can throw.  If 
 there is exactly one type of error, the most common way to handle it is by 
 returning optional.  If you have one obvious failure mode with a value, 
 then you throw that value.  The most common case is where you can throw 
 more than one sort of error, and therefore have an enum to describe it.
 
 3) Third, your enum needs to be declared fragile in order to allow clients 
 to enumerate their cases specifically.
 
 The third step (having to mark your enum fragile, however it is spelled) 
 is the biggest sign that you’re opting into a commitment that you should 
 think really hard about.  If folks don’t know that this is a big API 
 commitment, then we have bigger problems.
 
 
>> One thing you didn’t mention is that boxing thrown values in an 
>> existential requires allocation in the general case.  This may be 
>> unacceptable for some classes of Swift application (in the embedded / 
>> deep systems space) or simply undesirable because of the performance 
>> implication.
> 
> So, the performance implication cuts both ways.  We can design the ABI 
> for typed-throws so that, say, the callee initializes some buffer that's 
> passed into it.  That's an ABI that will kill some potential allocations 
> in deep systems code, no question about it.
 
 Agreed.
 
> But in non-deep-systems code, we generally expect that error types will 
> be resilient, which means that there are non-zero dynamic costs for 
> allocating space on the stack for the error.
 
 Proposed solution:  ABI is that the callee takes in a register which is 
 either a buffer address to fill in or null. On error, the callee returns 
 the error pointer in a specific register.  If there was a buffer passed 
 in, it uses it, otherwise it allocates.
 
 In practice, this allows the compiler to only pre-allocate the buffer when 
 it knows the fixed size, otherwise the caller allocates on the heap on 
 demand.
 
 AFAICT, the cost of this API is only a “li rN, 0” in the normal path.
>>> 
>>> If you really want zero mallocs, it seems to me like creative stack 
>>> accounting could get you there. In cases where we concretely know we're 
>>> working with an existential, we can "explode" the existential into an 
>>> opened type variable and a value of that type, and avoid allocating the 
>>> existential representation. In the case of an untyped or resilient error, 
>>> the callee could leave the error value and its type metadata on the stack 
>>> somewhere without resetting the stack pointer, stick the address of that 
>>> payload somewhere, and code would then propagate up to the catcher, who's 
>>> responsible for consuming the error value and popping the stack when it's 
>>> done with it. We could potentially do this more generally with existential 
>>> returns too, so that existentials are a viable abstraction tool even in 
>>> predictable-performance mode.
>>> 
>>> -Joe
>> 
>> Surely the compiler could already do that within a single 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-24 Thread Joe Groff via swift-evolution

> On Feb 23, 2017, at 10:36 PM, Russ Bishop  wrote:
> 
> 
>> On Feb 21, 2017, at 8:18 AM, Joe Groff > > wrote:
 
>>> 
>>> Yeah but Rust has a macro system so error-chain makes re-wrapping trivial. 
>>> A simple `chain_err(|| “my new error message”)` is enough to define and 
>>> “throw” a new error that wraps the underlying error.
>> 
>> That was my point—they recognized error chaining/layering as a common 
>> phonemenon and provided affordances for it as part of their design. I was 
>> saying we should do the same if we investigate typed errors seriously. (Not 
>> sure why you mentioned macros—chain_err is just a regular method, and could 
>> be defined as an extension method on Error in swift today.)
>> 
>> -Joe
> 
> Sorry, I meant error_chain! handles the boilerplate. At the end of the day 
> something has to create the new error types.
> 
> I agree with you on the chaining; if we really do want to go down this road 
> we need an elegant solution. Seeing people talk about getting rid of rethrows 
> makes me think this isn’t yet a serious discussion.

If `rethrows` can be turned into sugar for regular type parametricity, I think 
that'd be a win over our current model. The sugar is probably still useful 
since ` (() throws E -> ()) throws SomethingDerivedFrom -> ()` is common 
enough; whether SomethingDerivedFrom is E, Error, or 
MyModulesErrorWrapper would have to be discussed.

-Joe

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-23 Thread Joe Groff via swift-evolution

> On Feb 23, 2017, at 12:06 AM, Karl Wagner  wrote:
> 
> 
>> On 22 Feb 2017, at 21:13, Joe Groff  wrote:
>> 
>>> 
>>> On Feb 21, 2017, at 8:50 PM, Chris Lattner via swift-evolution 
>>>  wrote:
>>> 
>>> On Feb 20, 2017, at 11:12 PM, John McCall  wrote:
> As you know, I still think that adding typed throws is the right thing to 
> do.  I understand your concern about “the feature could be misused” but 
> the same thing is true about many other language features.
 
 That's fair, but I do think there's an important difference here.  The way 
 I see it, typed-throws is really something of an expert feature, not 
 because it's at all difficult to use, but the reverse: because it's easy 
 to use without really thinking about the consequences.  (And the benefits 
 are pretty subtle, too.)  I'm not saying that we should design it to be 
 hard to use, but I think maybe it shouldn't immediately suggest itself, 
 and it especially shouldn't come across as just a more specific version of 
 throws.
>>> 
>>> Yeah, I agree that it will be appealing to people who don’t know better, 
>>> but here’s the thing: the (almost certain) Swift design will prevent the 
>>> bad thing from happening in practice.
>>> 
>>> Consider the barriers Swift already puts in place to prevent the bad thing 
>>> (declaring an inappropriately narrow explicitly-specified throw signature) 
>>> from happening:
>>> 
>>> 1) First of all, you need to declare a public API.  If it isn’t public, 
>>> then there is no concern at all, you can evolve the implementation and 
>>> clients together.
>>> 
>>> 2) The Second problem depends on the number of errors it can throw.  If 
>>> there is exactly one type of error, the most common way to handle it is by 
>>> returning optional.  If you have one obvious failure mode with a value, 
>>> then you throw that value.  The most common case is where you can throw 
>>> more than one sort of error, and therefore have an enum to describe it.
>>> 
>>> 3) Third, your enum needs to be declared fragile in order to allow clients 
>>> to enumerate their cases specifically.
>>> 
>>> The third step (having to mark your enum fragile, however it is spelled) is 
>>> the biggest sign that you’re opting into a commitment that you should think 
>>> really hard about.  If folks don’t know that this is a big API commitment, 
>>> then we have bigger problems.
>>> 
>>> 
> One thing you didn’t mention is that boxing thrown values in an 
> existential requires allocation in the general case.  This may be 
> unacceptable for some classes of Swift application (in the embedded / 
> deep systems space) or simply undesirable because of the performance 
> implication.
 
 So, the performance implication cuts both ways.  We can design the ABI for 
 typed-throws so that, say, the callee initializes some buffer that's 
 passed into it.  That's an ABI that will kill some potential allocations 
 in deep systems code, no question about it.
>>> 
>>> Agreed.
>>> 
 But in non-deep-systems code, we generally expect that error types will be 
 resilient, which means that there are non-zero dynamic costs for 
 allocating space on the stack for the error.
>>> 
>>> Proposed solution:  ABI is that the callee takes in a register which is 
>>> either a buffer address to fill in or null.  On error, the callee returns 
>>> the error pointer in a specific register.  If there was a buffer passed in, 
>>> it uses it, otherwise it allocates.
>>> 
>>> In practice, this allows the compiler to only pre-allocate the buffer when 
>>> it knows the fixed size, otherwise the caller allocates on the heap on 
>>> demand.
>>> 
>>> AFAICT, the cost of this API is only a “li rN, 0” in the normal path.
>> 
>> If you really want zero mallocs, it seems to me like creative stack 
>> accounting could get you there. In cases where we concretely know we're 
>> working with an existential, we can "explode" the existential into an opened 
>> type variable and a value of that type, and avoid allocating the existential 
>> representation. In the case of an untyped or resilient error, the callee 
>> could leave the error value and its type metadata on the stack somewhere 
>> without resetting the stack pointer, stick the address of that payload 
>> somewhere, and code would then propagate up to the catcher, who's 
>> responsible for consuming the error value and popping the stack when it's 
>> done with it. We could potentially do this more generally with existential 
>> returns too, so that existentials are a viable abstraction tool even in 
>> predictable-performance mode.
>> 
>> -Joe
> 
> Surely the compiler could already do that within a single module? If it knows 
> it’s going to get a bunch of cases from the same enum, it can have an 
> optimised internal call. We could possibly allow exhaustive catching 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-23 Thread Karl Wagner via swift-evolution

> On 22 Feb 2017, at 21:13, Joe Groff  wrote:
> 
>> 
>> On Feb 21, 2017, at 8:50 PM, Chris Lattner via swift-evolution 
>>  wrote:
>> 
>> On Feb 20, 2017, at 11:12 PM, John McCall  wrote:
 As you know, I still think that adding typed throws is the right thing to 
 do.  I understand your concern about “the feature could be misused” but 
 the same thing is true about many other language features.
>>> 
>>> That's fair, but I do think there's an important difference here.  The way 
>>> I see it, typed-throws is really something of an expert feature, not 
>>> because it's at all difficult to use, but the reverse: because it's easy to 
>>> use without really thinking about the consequences.  (And the benefits are 
>>> pretty subtle, too.)  I'm not saying that we should design it to be hard to 
>>> use, but I think maybe it shouldn't immediately suggest itself, and it 
>>> especially shouldn't come across as just a more specific version of throws.
>> 
>> Yeah, I agree that it will be appealing to people who don’t know better, but 
>> here’s the thing: the (almost certain) Swift design will prevent the bad 
>> thing from happening in practice.
>> 
>> Consider the barriers Swift already puts in place to prevent the bad thing 
>> (declaring an inappropriately narrow explicitly-specified throw signature) 
>> from happening:
>> 
>> 1) First of all, you need to declare a public API.  If it isn’t public, then 
>> there is no concern at all, you can evolve the implementation and clients 
>> together.
>> 
>> 2) The Second problem depends on the number of errors it can throw.  If 
>> there is exactly one type of error, the most common way to handle it is by 
>> returning optional.  If you have one obvious failure mode with a value, then 
>> you throw that value.  The most common case is where you can throw more than 
>> one sort of error, and therefore have an enum to describe it.
>> 
>> 3) Third, your enum needs to be declared fragile in order to allow clients 
>> to enumerate their cases specifically.
>> 
>> The third step (having to mark your enum fragile, however it is spelled) is 
>> the biggest sign that you’re opting into a commitment that you should think 
>> really hard about.  If folks don’t know that this is a big API commitment, 
>> then we have bigger problems.
>> 
>> 
 One thing you didn’t mention is that boxing thrown values in an 
 existential requires allocation in the general case.  This may be 
 unacceptable for some classes of Swift application (in the embedded / deep 
 systems space) or simply undesirable because of the performance 
 implication.
>>> 
>>> So, the performance implication cuts both ways.  We can design the ABI for 
>>> typed-throws so that, say, the callee initializes some buffer that's passed 
>>> into it.  That's an ABI that will kill some potential allocations in deep 
>>> systems code, no question about it.
>> 
>> Agreed.
>> 
>>> But in non-deep-systems code, we generally expect that error types will be 
>>> resilient, which means that there are non-zero dynamic costs for allocating 
>>> space on the stack for the error.
>> 
>> Proposed solution:  ABI is that the callee takes in a register which is 
>> either a buffer address to fill in or null.  On error, the callee returns 
>> the error pointer in a specific register.  If there was a buffer passed in, 
>> it uses it, otherwise it allocates.
>> 
>> In practice, this allows the compiler to only pre-allocate the buffer when 
>> it knows the fixed size, otherwise the caller allocates on the heap on 
>> demand.
>> 
>> AFAICT, the cost of this API is only a “li rN, 0” in the normal path.
> 
> If you really want zero mallocs, it seems to me like creative stack 
> accounting could get you there. In cases where we concretely know we're 
> working with an existential, we can "explode" the existential into an opened 
> type variable and a value of that type, and avoid allocating the existential 
> representation. In the case of an untyped or resilient error, the callee 
> could leave the error value and its type metadata on the stack somewhere 
> without resetting the stack pointer, stick the address of that payload 
> somewhere, and code would then propagate up to the catcher, who's responsible 
> for consuming the error value and popping the stack when it's done with it. 
> We could potentially do this more generally with existential returns too, so 
> that existentials are a viable abstraction tool even in 
> predictable-performance mode.
> 
> -Joe

Surely the compiler could already do that within a single module? If it knows 
it’s going to get a bunch of cases from the same enum, it can have an optimised 
internal call. We could possibly allow exhaustive catching within the same 
module this way.

Most libraries will need the existential wrapper due to resilience concerns. 
Libraries that want to get this optimised, non-resilient, exhaustive 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-22 Thread Joe Groff via swift-evolution

> On Feb 21, 2017, at 8:50 PM, Chris Lattner via swift-evolution 
>  wrote:
> 
> On Feb 20, 2017, at 11:12 PM, John McCall  wrote:
>>> As you know, I still think that adding typed throws is the right thing to 
>>> do.  I understand your concern about “the feature could be misused” but the 
>>> same thing is true about many other language features.
>> 
>> That's fair, but I do think there's an important difference here.  The way I 
>> see it, typed-throws is really something of an expert feature, not because 
>> it's at all difficult to use, but the reverse: because it's easy to use 
>> without really thinking about the consequences.  (And the benefits are 
>> pretty subtle, too.)  I'm not saying that we should design it to be hard to 
>> use, but I think maybe it shouldn't immediately suggest itself, and it 
>> especially shouldn't come across as just a more specific version of throws.
> 
> Yeah, I agree that it will be appealing to people who don’t know better, but 
> here’s the thing: the (almost certain) Swift design will prevent the bad 
> thing from happening in practice.
> 
> Consider the barriers Swift already puts in place to prevent the bad thing 
> (declaring an inappropriately narrow explicitly-specified throw signature) 
> from happening:
> 
> 1) First of all, you need to declare a public API.  If it isn’t public, then 
> there is no concern at all, you can evolve the implementation and clients 
> together.
> 
> 2) The Second problem depends on the number of errors it can throw.  If there 
> is exactly one type of error, the most common way to handle it is by 
> returning optional.  If you have one obvious failure mode with a value, then 
> you throw that value.  The most common case is where you can throw more than 
> one sort of error, and therefore have an enum to describe it.
> 
> 3) Third, your enum needs to be declared fragile in order to allow clients to 
> enumerate their cases specifically.
> 
> The third step (having to mark your enum fragile, however it is spelled) is 
> the biggest sign that you’re opting into a commitment that you should think 
> really hard about.  If folks don’t know that this is a big API commitment, 
> then we have bigger problems.
> 
> 
>>> One thing you didn’t mention is that boxing thrown values in an existential 
>>> requires allocation in the general case.  This may be unacceptable for some 
>>> classes of Swift application (in the embedded / deep systems space) or 
>>> simply undesirable because of the performance implication.
>> 
>> So, the performance implication cuts both ways.  We can design the ABI for 
>> typed-throws so that, say, the callee initializes some buffer that's passed 
>> into it.  That's an ABI that will kill some potential allocations in deep 
>> systems code, no question about it.
> 
> Agreed.
> 
>> But in non-deep-systems code, we generally expect that error types will be 
>> resilient, which means that there are non-zero dynamic costs for allocating 
>> space on the stack for the error.
> 
> Proposed solution:  ABI is that the callee takes in a register which is 
> either a buffer address to fill in or null.  On error, the callee returns the 
> error pointer in a specific register.  If there was a buffer passed in, it 
> uses it, otherwise it allocates.
> 
> In practice, this allows the compiler to only pre-allocate the buffer when it 
> knows the fixed size, otherwise the caller allocates on the heap on demand.
> 
> AFAICT, the cost of this API is only a “li rN, 0” in the normal path.

If you really want zero mallocs, it seems to me like creative stack accounting 
could get you there. In cases where we concretely know we're working with an 
existential, we can "explode" the existential into an opened type variable and 
a value of that type, and avoid allocating the existential representation. In 
the case of an untyped or resilient error, the callee could leave the error 
value and its type metadata on the stack somewhere without resetting the stack 
pointer, stick the address of that payload somewhere, and code would then 
propagate up to the catcher, who's responsible for consuming the error value 
and popping the stack when it's done with it. We could potentially do this more 
generally with existential returns too, so that existentials are a viable 
abstraction tool even in predictable-performance mode.

-Joe
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-22 Thread Chris Lattner via swift-evolution
On Feb 22, 2017, at 6:32 AM, John McCall  wrote:
>> 
>> 3) Third, your enum needs to be declared fragile in order to allow clients 
>> to enumerate their cases specifically.
>> 
>> The third step (having to mark your enum fragile, however it is spelled) is 
>> the biggest sign that you’re opting into a commitment that you should think 
>> really hard about.  If folks don’t know that this is a big API commitment, 
>> then we have bigger problems.
> 
> You're imagining somebody adding a throws annotation because their function 
> itself has one or two explicit throw sites.

I was trying to imagine what the “bad thing” was that you were imagining, I 
guess I missed the mark :)

> In reality, it's probably because they're calling some other function that 
> can throw.  The low-impedence path in that case is to declare that you throw 
> whatever that function throws; this is why people talk about exceptions 
> encoding your library's dependency tree.

Yeah, I’ve totally seen that in Java and other languages, so I can see your 
concern.  I think that that concern is completely addressed by:

1) Only allow one thrown type.
2) Never add T | U | V syntax for anonymous enums.

Both of these seem strongly motivated by other design points in swift, so I 
took those for granted.

With the two of them together, Swift would seriously reduce the odds of your 
"bad thing” from happening, because people who have to write out new enum types 
to encode their dependency tree.

>> AFAICT, the cost of this API is only a “li rN, 0” in the normal path.
> 
> If you're willing to give up on a no-malloc guarantee, even if the fact of 
> resilience, yes, this and similar techniques would work.  I'm not sure that 
> all "deep systems" code is quite that deep.

I’m not claiming that all “deep systems” code (whatever that means) is quite 
"that deep", only that an interesting communities who consider themselves 
purveyors of systems code also care about controlling allocations for a variety 
of reasons.

The sort of systems code I’m imagining doesn’t care about resilience at all.  
They generally have a closed world assumption anyway.  

-Chris

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-22 Thread Matthew Johnson via swift-evolution

> On Feb 22, 2017, at 8:32 AM, John McCall via swift-evolution 
>  wrote:
> 
> 
>> On Feb 21, 2017, at 11:50 PM, Chris Lattner  wrote:
>> 
>> On Feb 20, 2017, at 11:12 PM, John McCall  wrote:
 As you know, I still think that adding typed throws is the right thing to 
 do.  I understand your concern about “the feature could be misused” but 
 the same thing is true about many other language features.
>>> 
>>> That's fair, but I do think there's an important difference here.  The way 
>>> I see it, typed-throws is really something of an expert feature, not 
>>> because it's at all difficult to use, but the reverse: because it's easy to 
>>> use without really thinking about the consequences.  (And the benefits are 
>>> pretty subtle, too.)  I'm not saying that we should design it to be hard to 
>>> use, but I think maybe it shouldn't immediately suggest itself, and it 
>>> especially shouldn't come across as just a more specific version of throws.
>> 
>> Yeah, I agree that it will be appealing to people who don’t know better, but 
>> here’s the thing: the (almost certain) Swift design will prevent the bad 
>> thing from happening in practice.
>> 
>> Consider the barriers Swift already puts in place to prevent the bad thing 
>> (declaring an inappropriately narrow explicitly-specified throw signature) 
>> from happening:
>> 
>> 1) First of all, you need to declare a public API.  If it isn’t public, then 
>> there is no concern at all, you can evolve the implementation and clients 
>> together.
>> 
>> 2) The Second problem depends on the number of errors it can throw.  If 
>> there is exactly one type of error, the most common way to handle it is by 
>> returning optional.  If you have one obvious failure mode with a value, then 
>> you throw that value.  The most common case is where you can throw more than 
>> one sort of error, and therefore have an enum to describe it.
>> 
>> 3) Third, your enum needs to be declared fragile in order to allow clients 
>> to enumerate their cases specifically.
>> 
>> The third step (having to mark your enum fragile, however it is spelled) is 
>> the biggest sign that you’re opting into a commitment that you should think 
>> really hard about.  If folks don’t know that this is a big API commitment, 
>> then we have bigger problems.
> 
> You're imagining somebody adding a throws annotation because their function 
> itself has one or two explicit throw sites.  In reality, it's probably 
> because they're calling some other function that can throw.  The 
> low-impedence path in that case is to declare that you throw whatever that 
> function throws; this is why people talk about exceptions encoding your 
> library's dependency tree.

A function should absolutely consider carefully how it propagates errors.  That 
doesn’t mean that it shouldn’t be allowed to provide an appropriate abstraction 
that bounds the errors it might throw.  We should try to make this easy to do.  
Sometimes `Error` will be the appropriate bound and other times it will be very 
useful to provide a more granular bound that is not simply “here’s everything 
my dependencies can throw”.  

Of course this power can be abused.  But by designing a system that makes doing 
the right thing ergonomic and educating the community about how to use it I 
think we can avoid the worst problems people have faced in other languages like 
Java.

> 
 One thing you didn’t mention is that boxing thrown values in an 
 existential requires allocation in the general case.  This may be 
 unacceptable for some classes of Swift application (in the embedded / deep 
 systems space) or simply undesirable because of the performance 
 implication.
>>> 
>>> So, the performance implication cuts both ways.  We can design the ABI for 
>>> typed-throws so that, say, the callee initializes some buffer that's passed 
>>> into it.  That's an ABI that will kill some potential allocations in deep 
>>> systems code, no question about it.
>> 
>> Agreed.
>> 
>>> But in non-deep-systems code, we generally expect that error types will be 
>>> resilient, which means that there are non-zero dynamic costs for allocating 
>>> space on the stack for the error.
>> 
>> Proposed solution:  ABI is that the callee takes in a register which is 
>> either a buffer address to fill in or null.  On error, the callee returns 
>> the error pointer in a specific register.  If there was a buffer passed in, 
>> it uses it, otherwise it allocates.
>> 
>> In practice, this allows the compiler to only pre-allocate the buffer when 
>> it knows the fixed size, otherwise the caller allocates on the heap on 
>> demand.
>> 
>> AFAICT, the cost of this API is only a “li rN, 0” in the normal path.
> 
> If you're willing to give up on a no-malloc guarantee, even if the fact of 
> resilience, yes, this and similar techniques would work.  I'm not sure that 
> all "deep systems" code is 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-22 Thread John McCall via swift-evolution

> On Feb 21, 2017, at 11:50 PM, Chris Lattner  wrote:
> 
> On Feb 20, 2017, at 11:12 PM, John McCall  wrote:
>>> As you know, I still think that adding typed throws is the right thing to 
>>> do.  I understand your concern about “the feature could be misused” but the 
>>> same thing is true about many other language features.
>> 
>> That's fair, but I do think there's an important difference here.  The way I 
>> see it, typed-throws is really something of an expert feature, not because 
>> it's at all difficult to use, but the reverse: because it's easy to use 
>> without really thinking about the consequences.  (And the benefits are 
>> pretty subtle, too.)  I'm not saying that we should design it to be hard to 
>> use, but I think maybe it shouldn't immediately suggest itself, and it 
>> especially shouldn't come across as just a more specific version of throws.
> 
> Yeah, I agree that it will be appealing to people who don’t know better, but 
> here’s the thing: the (almost certain) Swift design will prevent the bad 
> thing from happening in practice.
> 
> Consider the barriers Swift already puts in place to prevent the bad thing 
> (declaring an inappropriately narrow explicitly-specified throw signature) 
> from happening:
> 
> 1) First of all, you need to declare a public API.  If it isn’t public, then 
> there is no concern at all, you can evolve the implementation and clients 
> together.
> 
> 2) The Second problem depends on the number of errors it can throw.  If there 
> is exactly one type of error, the most common way to handle it is by 
> returning optional.  If you have one obvious failure mode with a value, then 
> you throw that value.  The most common case is where you can throw more than 
> one sort of error, and therefore have an enum to describe it.
> 
> 3) Third, your enum needs to be declared fragile in order to allow clients to 
> enumerate their cases specifically.
> 
> The third step (having to mark your enum fragile, however it is spelled) is 
> the biggest sign that you’re opting into a commitment that you should think 
> really hard about.  If folks don’t know that this is a big API commitment, 
> then we have bigger problems.

You're imagining somebody adding a throws annotation because their function 
itself has one or two explicit throw sites.  In reality, it's probably because 
they're calling some other function that can throw.  The low-impedence path in 
that case is to declare that you throw whatever that function throws; this is 
why people talk about exceptions encoding your library's dependency tree.

>>> One thing you didn’t mention is that boxing thrown values in an existential 
>>> requires allocation in the general case.  This may be unacceptable for some 
>>> classes of Swift application (in the embedded / deep systems space) or 
>>> simply undesirable because of the performance implication.
>> 
>> So, the performance implication cuts both ways.  We can design the ABI for 
>> typed-throws so that, say, the callee initializes some buffer that's passed 
>> into it.  That's an ABI that will kill some potential allocations in deep 
>> systems code, no question about it.
> 
> Agreed.
> 
>> But in non-deep-systems code, we generally expect that error types will be 
>> resilient, which means that there are non-zero dynamic costs for allocating 
>> space on the stack for the error.
> 
> Proposed solution:  ABI is that the callee takes in a register which is 
> either a buffer address to fill in or null.  On error, the callee returns the 
> error pointer in a specific register.  If there was a buffer passed in, it 
> uses it, otherwise it allocates.
> 
> In practice, this allows the compiler to only pre-allocate the buffer when it 
> knows the fixed size, otherwise the caller allocates on the heap on demand.
> 
> AFAICT, the cost of this API is only a “li rN, 0” in the normal path.

If you're willing to give up on a no-malloc guarantee, even if the fact of 
resilience, yes, this and similar techniques would work.  I'm not sure that all 
"deep systems" code is quite that deep.

John.
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-21 Thread Chris Lattner via swift-evolution
On Feb 20, 2017, at 11:12 PM, John McCall  wrote:
>> As you know, I still think that adding typed throws is the right thing to 
>> do.  I understand your concern about “the feature could be misused” but the 
>> same thing is true about many other language features.
> 
> That's fair, but I do think there's an important difference here.  The way I 
> see it, typed-throws is really something of an expert feature, not because 
> it's at all difficult to use, but the reverse: because it's easy to use 
> without really thinking about the consequences.  (And the benefits are pretty 
> subtle, too.)  I'm not saying that we should design it to be hard to use, but 
> I think maybe it shouldn't immediately suggest itself, and it especially 
> shouldn't come across as just a more specific version of throws.

Yeah, I agree that it will be appealing to people who don’t know better, but 
here’s the thing: the (almost certain) Swift design will prevent the bad thing 
from happening in practice.

Consider the barriers Swift already puts in place to prevent the bad thing 
(declaring an inappropriately narrow explicitly-specified throw signature) from 
happening:

1) First of all, you need to declare a public API.  If it isn’t public, then 
there is no concern at all, you can evolve the implementation and clients 
together.

2) The Second problem depends on the number of errors it can throw.  If there 
is exactly one type of error, the most common way to handle it is by returning 
optional.  If you have one obvious failure mode with a value, then you throw 
that value.  The most common case is where you can throw more than one sort of 
error, and therefore have an enum to describe it.

3) Third, your enum needs to be declared fragile in order to allow clients to 
enumerate their cases specifically.

The third step (having to mark your enum fragile, however it is spelled) is the 
biggest sign that you’re opting into a commitment that you should think really 
hard about.  If folks don’t know that this is a big API commitment, then we 
have bigger problems.


>> One thing you didn’t mention is that boxing thrown values in an existential 
>> requires allocation in the general case.  This may be unacceptable for some 
>> classes of Swift application (in the embedded / deep systems space) or 
>> simply undesirable because of the performance implication.
> 
> So, the performance implication cuts both ways.  We can design the ABI for 
> typed-throws so that, say, the callee initializes some buffer that's passed 
> into it.  That's an ABI that will kill some potential allocations in deep 
> systems code, no question about it.

Agreed.

>  But in non-deep-systems code, we generally expect that error types will be 
> resilient, which means that there are non-zero dynamic costs for allocating 
> space on the stack for the error.

Proposed solution:  ABI is that the callee takes in a register which is either 
a buffer address to fill in or null.  On error, the callee returns the error 
pointer in a specific register.  If there was a buffer passed in, it uses it, 
otherwise it allocates.

In practice, this allows the compiler to only pre-allocate the buffer when it 
knows the fixed size, otherwise the caller allocates on the heap on demand.

AFAICT, the cost of this API is only a “li rN, 0” in the normal path.

-Chris

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-21 Thread Joe Groff via swift-evolution

> On Feb 20, 2017, at 11:20 PM, Russ Bishop  wrote:
> 
>> 
>> On Feb 20, 2017, at 1:15 PM, Joe Groff via swift-evolution 
>>  wrote:
>> 
>> 
>>> On Feb 20, 2017, at 9:57 AM, John McCall via swift-evolution 
>>>  wrote:
>>> 
 On Feb 19, 2017, at 3:04 PM, Anton Zhilin via swift-evolution 
  wrote:
 It’s expected that if you need resilience, then you will throw an “open” 
 enum. Essentially, we pass resilience of typed throws on to those who will 
 hopefully establish resilience of enums.
 
 If you prefer separate error types, then declare a base protocol for all 
 your error types and throw a protocol existential. You won’t even need 
 default case in switches, if closed protocols make it into the language.
 
 I don’t like any solution that is based on comments. I think that compiler 
 should always ignore comments.
 
>>> I agree.  And in general, this sort of thing is exactly my core concern 
>>> about adding typed throws to the language: I am completely certain that 
>>> many programmers will add typed throws annotations because they're 
>>> programmers and thus, well, probably a little obsessive/compulsive, and 
>>> they're trying to precisely document the behavior of their function without 
>>> necessarily thinking about the usefulness of that information for their 
>>> clients and (if they're writing a library; and really you should almost 
>>> always be writing code as if you're writing a library) whether they're 
>>> actually willing to commit to that behavior in their interface.  For those 
>>> programmers, typed throws is just going to box them in and force them into 
>>> anti-patterns in the long run.
>>> 
>>> In the vast majority of use-cases, clients are not going to exhaustively 
>>> handle all errors — they will always have some generic fall-back.  That is 
>>> not pessimism, it's actually the natural result of the complicated world we 
>>> live in, where code can fail for a huge host of reasons and most callers 
>>> won't have meaningful special-case behavior for all of them.  (On most 
>>> operating systems, opening a file or a network connection can fail because 
>>> you ran out of file descriptors.  You're seriously telling me that you're 
>>> going to add a special case to your error logic for that?)  Go look at the 
>>> actual error types that people use in most typed-throws situations and try 
>>> to tell me I'm wrong — they probably have like twenty alternatives, and 
>>> solidly a quarter or more of them will just be embedding some other 
>>> arbitrarily-complex or stringly-typed error value.
>> 
>> I agree that overly specific API specs are a concern. There's still some 
>> documentational benefit to a typed error enum, even if it ultimately ends up 
>> containing a catch-all case, since it gives you the ability to also 
>> enumerate some number of discrete, realistically handleable cases. Sure, 
>> your network request running in an XPC service has infinitely many failure 
>> cases due to resource constraints or IPC failure or network chaos, but 
>> there's usually some number of domain-specific error cases  too. And if we 
>> compare the proposed "you get to specify one error enum type" model to, say, 
>> Java or C++98's "you get to specify any number of error classes" model, I 
>> think that helps steer people away from the most grievous API mistakes in 
>> Java land, since instead of listing a closed set of concrete error classes 
>> directly in your API signature, you'll list those error cases in your enum 
>> definition, and in a resilient world, the enum will be "open" by default to 
>> external users, preventing it from being a permanent liability unless the 
>> user explicitly opted into closedness.
> 
> Realistically there are only rarely actionable error cases and usually only 
> one or two; everything else just bubbles up to the top of the current 
> operation where you retry or report a permanent failure. If the enum is open 
> then you need a default catch-all error handling case anyway, so what benefit 
> is there to typed throws that you don’t get with documentation comments?
> 
> I’m positive John is right about what will happen in practice.

Perhaps. Doc comments have all the usual problems of documentation not being 
compiled code so drifting out of date with implementation. While an open error 
type wouldn't guarantee that the errors declared are the ones thrown, it'll at 
least guarantee that they exist.

>> 
>> In the discussions around Rust's error handling conventions, they recognized 
>> this pattern of APIs either raising some number of layer-appropriate errors 
>> or carrying forward the failure modes of the layers below them, and they 
>> developed a convention for errors wrapping other errors. It would be 
>> reasonable to say we should do the same thing as part of the typed errors 
>> design. If we were 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-21 Thread Karl Wagner via swift-evolution

> On 21 Feb 2017, at 00:34, Karl Wagner  wrote:
> 
> 
>> On 19 Feb 2017, at 21:04, Anton Zhilin > > wrote:
>> 
>> It’s expected that if you need resilience, then you will throw an “open” 
>> enum. Essentially, we pass resilience of typed throws on to those who will 
>> hopefully establish resilience of enums.
>> 
>> If you prefer separate error types, then declare a base protocol for all 
>> your error types and throw a protocol existential. You won’t even need 
>> default case in switches, if closed protocols make it into the language.
>> 
>> I don’t like any solution that is based on comments. I think that compiler 
>> should always ignore comments.
>> 
>> 
> 
> Open enums can only add cases, not remove them. That means that new versions 
> of a function will similarly only be able to add errors, and won’t be able to 
> communicate that certain errors are no longer thrown. Protocols aren’t a 
> solution, because you will need to write verbose conformances for all of your 
> Error types.
> 
> Let me put this in another (perhaps more palatable) way. Forget comments, 
> let’s say its part of some hidden metadata:
> 
> - The compiler lists every error which can be thrown by a function (it’s 
> easily able to do that)
> - Rather than making this part of the signature, the signature only says 
> “throws an Error” and the actual Error list is written somewhere in the 
> module as documentation/metadata.
> 
> Here’s why it’s so good:
> 
> - It’s completely free (you don’t write anything). The compiler generates 
> everything for you.
> - It’s **completely optional**: it won’t make your structure your Errors in a 
> way that's less usable in a type-system sense for clients who don’t care 
> about exhaustive catching.
> - Exhaustive catching within your own module for free (you can omit a 
> catch-all, the compiler won’t complain)
> 
> It’s good for resiliency, too:
> 
> - Non-resilient functions always reserve the right to throw new Errors in 
> later versions. No system will get exhaustive catching for them anyway.
> - If you stop throwing a specific Error, nothing breaks - it simply vanishes 
> from the documentation/metadata. The compiler can simply warn about the 
> redundant catch.
> - Resilient (@versioned) functions can still offer exhaustive catching if we 
> want to offer that. We might decide to make that opt-in/opt-out, because it 
> would mean they would be limited to removing Errors, and never adding new 
> ones.
> —> As with any ABI promise, we will trap or it will be UB if you break the 
> contract. We couldn’t validate it when compiling, but theoretically a 
> validator could be built which compared two library versions.
> 
> - Karl
> 


So here’s my counter-proposal, fleshed out:

Specially, on resiliency:

## Internally to a module

Compiler can use generated Error-list metadata to:
- provide compile-errors with specific uncaught errors (better diagnostics)
- allow omitting catch-alls
- optimise away Error existential allocations

All of that would automatically apply to all throwing/rethrowing functions, 
without any additional developer effort.

## Cross-module

Compiler can use generated Error-list metadata to:
- Inform users about errors that might get thrown by this version of the 
function (purely documentation)
- Allow omitting catch-alls for specific functions which opt-in to that 
contract.

And that’s it. Notice there is no behavioural change; the Error-list metadata 
is entirely optional.

### Exhaustive catching cross-module

Resilient functions can _additionally_ promise to never throw new Errors. It 
should be an additional promise. From an error-list perspective, the function 
makes an additional promise that later versions of the error-list will never 
get more inclusive.

We can’t check that resilience at compile-time, though. If you change the 
function signature, you will get a error in the dynamic linker. Similarly, if 
somebody just adds a case to their @fixed enum, you won’t know until runtime 
when it gets thrown and nobody’s there to catch it.

- It would be cool if we failed gracefully in that case; if the caller wasn’t 
catching exhaustively, the new error should fall in to the existing catch-all.
- Otherwise, if the caller was assuming the library author kept their promise 
and omitted a catch-all, we should still synthesise one to provide a unique 
trap location (something like swift_resilience_unexpectedError()).
- It means we can't optimise away the Error existential cross-module (maybe 
in -Ounchecked?), but that seems to me like an acceptable cost.

The big problem with this is that it relies on unwritten, generated metadata. 
For resilient functions promising resilient error-lists, it’s helpful to have 
the errors you’re promising written down and easily manually inspectable. 
That’s why I initially suggested having the compiler validate the documentation 
comments. We could 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread Russ Bishop via swift-evolution

> On Feb 20, 2017, at 1:15 PM, Joe Groff via swift-evolution 
>  wrote:
> 
> 
>> On Feb 20, 2017, at 9:57 AM, John McCall via swift-evolution 
>> > wrote:
>> 
>>> On Feb 19, 2017, at 3:04 PM, Anton Zhilin via swift-evolution 
>>> > wrote:
>>> It’s expected that if you need resilience, then you will throw an “open” 
>>> enum. Essentially, we pass resilience of typed throws on to those who will 
>>> hopefully establish resilience of enums.
>>> 
>>> If you prefer separate error types, then declare a base protocol for all 
>>> your error types and throw a protocol existential. You won’t even need 
>>> default case in switches, if closed protocols make it into the language.
>>> 
>>> I don’t like any solution that is based on comments. I think that compiler 
>>> should always ignore comments.
>>> 
>> I agree.  And in general, this sort of thing is exactly my core concern 
>> about adding typed throws to the language: I am completely certain that many 
>> programmers will add typed throws annotations because they're programmers 
>> and thus, well, probably a little obsessive/compulsive, and they're trying 
>> to precisely document the behavior of their function without necessarily 
>> thinking about the usefulness of that information for their clients and (if 
>> they're writing a library; and really you should almost always be writing 
>> code as if you're writing a library) whether they're actually willing to 
>> commit to that behavior in their interface.  For those programmers, typed 
>> throws is just going to box them in and force them into anti-patterns in the 
>> long run.
>> 
>> In the vast majority of use-cases, clients are not going to exhaustively 
>> handle all errors — they will always have some generic fall-back.  That is 
>> not pessimism, it's actually the natural result of the complicated world we 
>> live in, where code can fail for a huge host of reasons and most callers 
>> won't have meaningful special-case behavior for all of them.  (On most 
>> operating systems, opening a file or a network connection can fail because 
>> you ran out of file descriptors.  You're seriously telling me that you're 
>> going to add a special case to your error logic for that?)  Go look at the 
>> actual error types that people use in most typed-throws situations and try 
>> to tell me I'm wrong — they probably have like twenty alternatives, and 
>> solidly a quarter or more of them will just be embedding some other 
>> arbitrarily-complex or stringly-typed error value.
> 
> I agree that overly specific API specs are a concern. There's still some 
> documentational benefit to a typed error enum, even if it ultimately ends up 
> containing a catch-all case, since it gives you the ability to also enumerate 
> some number of discrete, realistically handleable cases. Sure, your network 
> request running in an XPC service has infinitely many failure cases due to 
> resource constraints or IPC failure or network chaos, but there's usually 
> some number of domain-specific error cases  too. And if we compare the 
> proposed "you get to specify one error enum type" model to, say, Java or 
> C++98's "you get to specify any number of error classes" model, I think that 
> helps steer people away from the most grievous API mistakes in Java land, 
> since instead of listing a closed set of concrete error classes directly in 
> your API signature, you'll list those error cases in your enum definition, 
> and in a resilient world, the enum will be "open" by default to external 
> users, preventing it from being a permanent liability unless the user 
> explicitly opted into closedness.

Realistically there are only rarely actionable error cases and usually only one 
or two; everything else just bubbles up to the top of the current operation 
where you retry or report a permanent failure. If the enum is open then you 
need a default catch-all error handling case anyway, so what benefit is there 
to typed throws that you don’t get with documentation comments?

I’m positive John is right about what will happen in practice.

> 
> In the discussions around Rust's error handling conventions, they recognized 
> this pattern of APIs either raising some number of layer-appropriate errors 
> or carrying forward the failure modes of the layers below them, and they 
> developed a convention for errors wrapping other errors. It would be 
> reasonable to say we should do the same thing as part of the typed errors 
> design. If we were to generalize enum subtyping beyond Optional, that might 
> be one way to go about it, letting an enum wrap its underlying layers' 
> failure cases as subtypes.
> 
> -Joe

Yeah but Rust has a macro system so error-chain makes re-wrapping trivial. A 
simple `chain_err(|| “my new error message”)` is enough to define and “throw” a 
new error that wraps the 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread John McCall via swift-evolution
> On Feb 20, 2017, at 6:04 PM, Chris Lattner  wrote:
>> On Feb 20, 2017, at 9:57 AM, John McCall via swift-evolution 
>> > wrote:
>> 
>>> On Feb 19, 2017, at 3:04 PM, Anton Zhilin via swift-evolution 
>>> > wrote:
>>> It’s expected that if you need resilience, then you will throw an “open” 
>>> enum. Essentially, we pass resilience of typed throws on to those who will 
>>> hopefully establish resilience of enums.
>>> 
>>> If you prefer separate error types, then declare a base protocol for all 
>>> your error types and throw a protocol existential. You won’t even need 
>>> default case in switches, if closed protocols make it into the language.
>>> 
>>> I don’t like any solution that is based on comments. I think that compiler 
>>> should always ignore comments.
>>> 
>> I agree.  And in general, this sort of thing is exactly my core concern 
>> about adding typed throws to the language: I am completely certain that many 
>> programmers will add typed throws annotations because they're programmers 
>> and thus, well, probably a little obsessive/compulsive, and they're trying 
>> to precisely document the behavior of their function without necessarily 
>> thinking about the usefulness of that information for their clients and (if 
>> they're writing a library; and really you should almost always be writing 
>> code as if you're writing a library) whether they're actually willing to 
>> commit to that behavior in their interface.  For those programmers, typed 
>> throws is just going to box them in and force them into anti-patterns in the 
>> long run.
> 
> As you know, I still think that adding typed throws is the right thing to do. 
>  I understand your concern about “the feature could be misused” but the same 
> thing is true about many other language features.

That's fair, but I do think there's an important difference here.  The way I 
see it, typed-throws is really something of an expert feature, not because it's 
at all difficult to use, but the reverse: because it's easy to use without 
really thinking about the consequences.  (And the benefits are pretty subtle, 
too.)  I'm not saying that we should design it to be hard to use, but I think 
maybe it shouldn't immediately suggest itself, and it especially shouldn't come 
across as just a more specific version of throws.

> One thing you didn’t mention is that boxing thrown values in an existential 
> requires allocation in the general case.  This may be unacceptable for some 
> classes of Swift application (in the embedded / deep systems space) or simply 
> undesirable because of the performance implication.

So, the performance implication cuts both ways.  We can design the ABI for 
typed-throws so that, say, the callee initializes some buffer that's passed 
into it.  That's an ABI that will kill some potential allocations in deep 
systems code, no question about it.  But in non-deep-systems code, we generally 
expect that error types will be resilient, which means that there are non-zero 
dynamic costs for allocating space on the stack for the error.  (You can trade 
code size for performance, but the slow path is a function call to trigger the 
runtime layout of the type plus a couple of loads.) That space has to be 
allocated eagerly before the call, so it's paid even in the non-throwing path.  
And I can't think of anything we could do to shift that cost to the throw-site 
without either using a fixed-size buffer, requiring a second stack for the 
thread, or inventing some crazy CC where stack depth can change dynamically 
across a call.

John.___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread Russ Bishop via swift-evolution

> On Feb 19, 2017, at 12:04 PM, Anton Zhilin via swift-evolution 
>  wrote:
> 
> It’s expected that if you need resilience, then you will throw an “open” 
> enum. Essentially, we pass resilience of typed throws on to those who will 
> hopefully establish resilience of enums.
> 
You’ve just re-invented the error handling model we have today. 


> If you prefer separate error types, then declare a base protocol for all your 
> error types and throw a protocol existential. 
> 
And now we’ve re-invented Java’s checked exceptions “throws Exception”. 


To what end?

Russ


___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread John McCall via swift-evolution
> On Feb 20, 2017, at 4:15 PM, Joe Groff  wrote:
>> On Feb 20, 2017, at 9:57 AM, John McCall via swift-evolution 
>> > wrote:
>>> On Feb 19, 2017, at 3:04 PM, Anton Zhilin via swift-evolution 
>>> > wrote:
>>> It’s expected that if you need resilience, then you will throw an “open” 
>>> enum. Essentially, we pass resilience of typed throws on to those who will 
>>> hopefully establish resilience of enums.
>>> 
>>> If you prefer separate error types, then declare a base protocol for all 
>>> your error types and throw a protocol existential. You won’t even need 
>>> default case in switches, if closed protocols make it into the language.
>>> 
>>> I don’t like any solution that is based on comments. I think that compiler 
>>> should always ignore comments.
>>> 
>> I agree.  And in general, this sort of thing is exactly my core concern 
>> about adding typed throws to the language: I am completely certain that many 
>> programmers will add typed throws annotations because they're programmers 
>> and thus, well, probably a little obsessive/compulsive, and they're trying 
>> to precisely document the behavior of their function without necessarily 
>> thinking about the usefulness of that information for their clients and (if 
>> they're writing a library; and really you should almost always be writing 
>> code as if you're writing a library) whether they're actually willing to 
>> commit to that behavior in their interface.  For those programmers, typed 
>> throws is just going to box them in and force them into anti-patterns in the 
>> long run.
>> 
>> In the vast majority of use-cases, clients are not going to exhaustively 
>> handle all errors — they will always have some generic fall-back.  That is 
>> not pessimism, it's actually the natural result of the complicated world we 
>> live in, where code can fail for a huge host of reasons and most callers 
>> won't have meaningful special-case behavior for all of them.  (On most 
>> operating systems, opening a file or a network connection can fail because 
>> you ran out of file descriptors.  You're seriously telling me that you're 
>> going to add a special case to your error logic for that?)  Go look at the 
>> actual error types that people use in most typed-throws situations and try 
>> to tell me I'm wrong — they probably have like twenty alternatives, and 
>> solidly a quarter or more of them will just be embedding some other 
>> arbitrarily-complex or stringly-typed error value.
> 
> I agree that overly specific API specs are a concern. There's still some 
> documentational benefit to a typed error enum, even if it ultimately ends up 
> containing a catch-all case, since it gives you the ability to also enumerate 
> some number of discrete, realistically handleable cases. Sure, your network 
> request running in an XPC service has infinitely many failure cases due to 
> resource constraints or IPC failure or network chaos, but there's usually 
> some number of domain-specific error cases  too.

I definitely agree with the documentational benefits of declaring interesting 
error cases, and I agree that most libraries will add new domain-specific 
errors, too.  I just don't see the actual benefit to either API authors or 
clients of saying "I will only throw errors that look like *this*" and then 
*this* turns out to be an enum with ten interesting cases, most of which don't 
apply to the specific operation, and a bunch of generic wrapping cases that 
communicate very little; especially not compared to saying "in this interesting 
case I'll throw a FooError.bar" in the documentation.  Especially since it's 
not obvious that *cases* are the right granularity for describing a specific 
kind of domain error, since a case can't be retroactively split into more- and 
less-informative sub-cases without adding language features that only I seem to 
be interested in. :)

> And if we compare the proposed "you get to specify one error enum type" model 
> to, say, Java or C++98's "you get to specify any number of error classes" 
> model, I think that helps steer people away from the most grievous API 
> mistakes in Java land, since instead of listing a closed set of concrete 
> error classes directly in your API signature, you'll list those error cases 
> in your enum definition, and in a resilient world, the enum will be "open" by 
> default to external users, preventing it from being a permanent liability 
> unless the user explicitly opted into closedness.

To be exact, resilience prevents it from being a permanent liability as long as 
you've thought ahead enough to use your own error type.

> In the discussions around Rust's error handling conventions, they recognized 
> this pattern of APIs either raising some number of layer-appropriate errors 
> or carrying forward the failure modes of the layers below them, and they 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread Karl Wagner via swift-evolution

> On 19 Feb 2017, at 21:04, Anton Zhilin  wrote:
> 
> It’s expected that if you need resilience, then you will throw an “open” 
> enum. Essentially, we pass resilience of typed throws on to those who will 
> hopefully establish resilience of enums.
> 
> If you prefer separate error types, then declare a base protocol for all your 
> error types and throw a protocol existential. You won’t even need default 
> case in switches, if closed protocols make it into the language.
> 
> I don’t like any solution that is based on comments. I think that compiler 
> should always ignore comments.
> 
> 

Open enums can only add cases, not remove them. That means that new versions of 
a function will similarly only be able to add errors, and won’t be able to 
communicate that certain errors are no longer thrown. Protocols aren’t a 
solution, because you will need to write verbose conformances for all of your 
Error types.

Let me put this in another (perhaps more palatable) way. Forget comments, let’s 
say its part of some hidden metadata:

- The compiler lists every error which can be thrown by a function (it’s easily 
able to do that)
- Rather than making this part of the signature, the signature only says 
“throws an Error” and the actual Error list is written somewhere in the module 
as documentation/metadata.

Here’s why it’s so good:

- It’s completely free (you don’t write anything). The compiler generates 
everything for you.
- It’s **completely optional**: it won’t make your structure your Errors in a 
way that's less usable in a type-system sense for clients who don’t care about 
exhaustive catching.
- Exhaustive catching within your own module for free (you can omit a 
catch-all, the compiler won’t complain)

It’s good for resiliency, too:

- Non-resilient functions always reserve the right to throw new Errors in later 
versions. No system will get exhaustive catching for them anyway.
- If you stop throwing a specific Error, nothing breaks - it simply vanishes 
from the documentation/metadata. The compiler can simply warn about the 
redundant catch.
- Resilient (@versioned) functions can still offer exhaustive catching if we 
want to offer that. We might decide to make that opt-in/opt-out, because it 
would mean they would be limited to removing Errors, and never adding new ones.
—> As with any ABI promise, we will trap or it will be UB if you break the 
contract. We couldn’t validate it when compiling, but theoretically a validator 
could be built which compared two library versions.

- Karl

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread Chris Lattner via swift-evolution
> On Feb 20, 2017, at 9:57 AM, John McCall via swift-evolution 
> > wrote:
> 
>> On Feb 19, 2017, at 3:04 PM, Anton Zhilin via swift-evolution 
>> > wrote:
>> It’s expected that if you need resilience, then you will throw an “open” 
>> enum. Essentially, we pass resilience of typed throws on to those who will 
>> hopefully establish resilience of enums.
>> 
>> If you prefer separate error types, then declare a base protocol for all 
>> your error types and throw a protocol existential. You won’t even need 
>> default case in switches, if closed protocols make it into the language.
>> 
>> I don’t like any solution that is based on comments. I think that compiler 
>> should always ignore comments.
>> 
> I agree.  And in general, this sort of thing is exactly my core concern about 
> adding typed throws to the language: I am completely certain that many 
> programmers will add typed throws annotations because they're programmers and 
> thus, well, probably a little obsessive/compulsive, and they're trying to 
> precisely document the behavior of their function without necessarily 
> thinking about the usefulness of that information for their clients and (if 
> they're writing a library; and really you should almost always be writing 
> code as if you're writing a library) whether they're actually willing to 
> commit to that behavior in their interface.  For those programmers, typed 
> throws is just going to box them in and force them into anti-patterns in the 
> long run.

As you know, I still think that adding typed throws is the right thing to do.  
I understand your concern about “the feature could be misused” but the same 
thing is true about many other language features.

One thing you didn’t mention is that boxing thrown values in an existential 
requires allocation in the general case.  This may be unacceptable for some 
classes of Swift application (in the embedded / deep systems space) or simply 
undesirable because of the performance implication.

-Chris

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread John McCall via swift-evolution
> On Feb 20, 2017, at 3:46 PM, Karl Wagner  wrote:
>> On 20 Feb 2017, at 18:57, John McCall > > wrote:
>> 
>>> On Feb 19, 2017, at 3:04 PM, Anton Zhilin via swift-evolution 
>>> > wrote:
>>> It’s expected that if you need resilience, then you will throw an “open” 
>>> enum. Essentially, we pass resilience of typed throws on to those who will 
>>> hopefully establish resilience of enums.
>>> 
>>> If you prefer separate error types, then declare a base protocol for all 
>>> your error types and throw a protocol existential. You won’t even need 
>>> default case in switches, if closed protocols make it into the language.
>>> 
>>> I don’t like any solution that is based on comments. I think that compiler 
>>> should always ignore comments.
>>> 
>> I agree.  And in general, this sort of thing is exactly my core concern 
>> about adding typed throws to the language: I am completely certain that many 
>> programmers will add typed throws annotations because they're programmers 
>> and thus, well, probably a little obsessive/compulsive, and they're trying 
>> to precisely document the behavior of their function without necessarily 
>> thinking about the usefulness of that information for their clients and (if 
>> they're writing a library; and really you should almost always be writing 
>> code as if you're writing a library) whether they're actually willing to 
>> commit to that behavior in their interface.  For those programmers, typed 
>> throws is just going to box them in and force them into anti-patterns in the 
>> long run.
>> 
>> In the vast majority of use-cases, clients are not going to exhaustively 
>> handle all errors — they will always have some generic fall-back.  That is 
>> not pessimism, it's actually the natural result of the complicated world we 
>> live in, where code can fail for a huge host of reasons and most callers 
>> won't have meaningful special-case behavior for all of them.  (On most 
>> operating systems, opening a file or a network connection can fail because 
>> you ran out of file descriptors.  You're seriously telling me that you're 
>> going to add a special case to your error logic for that?)  Go look at the 
>> actual error types that people use in most typed-throws situations and try 
>> to tell me I'm wrong — they probably have like twenty alternatives, and 
>> solidly a quarter or more of them will just be embedding some other 
>> arbitrarily-complex or stringly-typed error value.
>> 
>> The real use case for typed throws is when you have something like a parser 
>> library that really does only fail in a fixed number of semantically 
>> distinct ways, and you both (1) actually care about enforcing that in the 
>> implementation and making sure that other errors are handled internally and 
>> (2) you really do expect clients to exhaustively switch over the error at 
>> some point.  That's important.  But I continue to think that if adding 
>> better support for that use case misleads other programmers into thinking 
>> they should use typed throws, we will have made the language worse overall.
>> 
>> John.
>> 
>> 
>> 
>>> 2017-02-18 18:27 GMT+03:00 Karl Wagner >> >:
>>> 
>>> 
>>> 
>>> 
>>> So, I’m not sure about what was decided last time, but my issues with this 
>>> are:
>>> 
>>> - The thrown error type will become part of the ABI of the function. If you 
>>> change the type of Error that is thrown, callers may not catch it. At the 
>>> same time, if we make enums resilient by default and only allow specifying 
>>> a single entire type, you will basically need one Error enum per function 
>>> and it will need to be @fixed if you actually want to remove the catch-all 
>>> block. Otherwise:
>>> 
>>> // Let’s say this isn’t @fixed...
>>> enum CanFailError {
>>> errorOne
>>> errorTwo
>>> }
>>> 
>>> func canFail() throws(CanFailError) { /* … */ }
>>> 
>>> do { try canFail() }
>>> catch CanFailError {
>>> switch error {
>>> case .errorOne: /* handle error one */
>>> case .errorTwo: /* handle error two */
>>> default:/* handle possible new errors in later versions of 
>>> the library */
>>> }
>>> }
>>> 
>>> do { try canFail() }
>>> catch .errorOne { /* handle error one */ }
>>> catch .errorTwo { /* handle error two */  }
>>> catch   { /* handle possible new errors in later versions of the 
>>> library */ }
>>> 
>>> - I usually have _semantic_ namespaces for Errors, rather than single types 
>>> per implementation pattern. If we are adding strong annotations about which 
>>> errors can be thrown, I’d quite like to incorporate that pattern. For 
>>> example:
>>> 
>>> extension File {
>>> @fixed enum OpeningError { 
>>> case .invalidPath
>>> case .accessDenied  // e.g. asking for write permissions for read-only 
>>> file
>>> }
>>> @fixed 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread Joe Groff via swift-evolution

> On Feb 20, 2017, at 9:57 AM, John McCall via swift-evolution 
>  wrote:
> 
>> On Feb 19, 2017, at 3:04 PM, Anton Zhilin via swift-evolution 
>> > wrote:
>> It’s expected that if you need resilience, then you will throw an “open” 
>> enum. Essentially, we pass resilience of typed throws on to those who will 
>> hopefully establish resilience of enums.
>> 
>> If you prefer separate error types, then declare a base protocol for all 
>> your error types and throw a protocol existential. You won’t even need 
>> default case in switches, if closed protocols make it into the language.
>> 
>> I don’t like any solution that is based on comments. I think that compiler 
>> should always ignore comments.
>> 
> I agree.  And in general, this sort of thing is exactly my core concern about 
> adding typed throws to the language: I am completely certain that many 
> programmers will add typed throws annotations because they're programmers and 
> thus, well, probably a little obsessive/compulsive, and they're trying to 
> precisely document the behavior of their function without necessarily 
> thinking about the usefulness of that information for their clients and (if 
> they're writing a library; and really you should almost always be writing 
> code as if you're writing a library) whether they're actually willing to 
> commit to that behavior in their interface.  For those programmers, typed 
> throws is just going to box them in and force them into anti-patterns in the 
> long run.
> 
> In the vast majority of use-cases, clients are not going to exhaustively 
> handle all errors — they will always have some generic fall-back.  That is 
> not pessimism, it's actually the natural result of the complicated world we 
> live in, where code can fail for a huge host of reasons and most callers 
> won't have meaningful special-case behavior for all of them.  (On most 
> operating systems, opening a file or a network connection can fail because 
> you ran out of file descriptors.  You're seriously telling me that you're 
> going to add a special case to your error logic for that?)  Go look at the 
> actual error types that people use in most typed-throws situations and try to 
> tell me I'm wrong — they probably have like twenty alternatives, and solidly 
> a quarter or more of them will just be embedding some other 
> arbitrarily-complex or stringly-typed error value.

I agree that overly specific API specs are a concern. There's still some 
documentational benefit to a typed error enum, even if it ultimately ends up 
containing a catch-all case, since it gives you the ability to also enumerate 
some number of discrete, realistically handleable cases. Sure, your network 
request running in an XPC service has infinitely many failure cases due to 
resource constraints or IPC failure or network chaos, but there's usually some 
number of domain-specific error cases  too. And if we compare the proposed "you 
get to specify one error enum type" model to, say, Java or C++98's "you get to 
specify any number of error classes" model, I think that helps steer people 
away from the most grievous API mistakes in Java land, since instead of listing 
a closed set of concrete error classes directly in your API signature, you'll 
list those error cases in your enum definition, and in a resilient world, the 
enum will be "open" by default to external users, preventing it from being a 
permanent liability unless the user explicitly opted into closedness.

In the discussions around Rust's error handling conventions, they recognized 
this pattern of APIs either raising some number of layer-appropriate errors or 
carrying forward the failure modes of the layers below them, and they developed 
a convention for errors wrapping other errors. It would be reasonable to say we 
should do the same thing as part of the typed errors design. If we were to 
generalize enum subtyping beyond Optional, that might be one way to go about 
it, letting an enum wrap its underlying layers' failure cases as subtypes.

-Joe

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread Karl Wagner via swift-evolution

> On 20 Feb 2017, at 18:57, John McCall  wrote:
> 
>> On Feb 19, 2017, at 3:04 PM, Anton Zhilin via swift-evolution 
>> > wrote:
>> It’s expected that if you need resilience, then you will throw an “open” 
>> enum. Essentially, we pass resilience of typed throws on to those who will 
>> hopefully establish resilience of enums.
>> 
>> If you prefer separate error types, then declare a base protocol for all 
>> your error types and throw a protocol existential. You won’t even need 
>> default case in switches, if closed protocols make it into the language.
>> 
>> I don’t like any solution that is based on comments. I think that compiler 
>> should always ignore comments.
>> 
> I agree.  And in general, this sort of thing is exactly my core concern about 
> adding typed throws to the language: I am completely certain that many 
> programmers will add typed throws annotations because they're programmers and 
> thus, well, probably a little obsessive/compulsive, and they're trying to 
> precisely document the behavior of their function without necessarily 
> thinking about the usefulness of that information for their clients and (if 
> they're writing a library; and really you should almost always be writing 
> code as if you're writing a library) whether they're actually willing to 
> commit to that behavior in their interface.  For those programmers, typed 
> throws is just going to box them in and force them into anti-patterns in the 
> long run.
> 
> In the vast majority of use-cases, clients are not going to exhaustively 
> handle all errors — they will always have some generic fall-back.  That is 
> not pessimism, it's actually the natural result of the complicated world we 
> live in, where code can fail for a huge host of reasons and most callers 
> won't have meaningful special-case behavior for all of them.  (On most 
> operating systems, opening a file or a network connection can fail because 
> you ran out of file descriptors.  You're seriously telling me that you're 
> going to add a special case to your error logic for that?)  Go look at the 
> actual error types that people use in most typed-throws situations and try to 
> tell me I'm wrong — they probably have like twenty alternatives, and solidly 
> a quarter or more of them will just be embedding some other 
> arbitrarily-complex or stringly-typed error value.
> 
> The real use case for typed throws is when you have something like a parser 
> library that really does only fail in a fixed number of semantically distinct 
> ways, and you both (1) actually care about enforcing that in the 
> implementation and making sure that other errors are handled internally and 
> (2) you really do expect clients to exhaustively switch over the error at 
> some point.  That's important.  But I continue to think that if adding better 
> support for that use case misleads other programmers into thinking they 
> should use typed throws, we will have made the language worse overall.
> 
> John.
> 
> 
> 
>> 2017-02-18 18:27 GMT+03:00 Karl Wagner > >:
>> 
>> 
>> 
>> 
>> So, I’m not sure about what was decided last time, but my issues with this 
>> are:
>> 
>> - The thrown error type will become part of the ABI of the function. If you 
>> change the type of Error that is thrown, callers may not catch it. At the 
>> same time, if we make enums resilient by default and only allow specifying a 
>> single entire type, you will basically need one Error enum per function and 
>> it will need to be @fixed if you actually want to remove the catch-all 
>> block. Otherwise:
>> 
>> // Let’s say this isn’t @fixed...
>> enum CanFailError {
>> errorOne
>> errorTwo
>> }
>> 
>> func canFail() throws(CanFailError) { /* … */ }
>> 
>> do { try canFail() }
>> catch CanFailError {
>> switch error {
>> case .errorOne: /* handle error one */
>> case .errorTwo: /* handle error two */
>> default:/* handle possible new errors in later versions of 
>> the library */
>> }
>> }
>> 
>> do { try canFail() }
>> catch .errorOne { /* handle error one */ }
>> catch .errorTwo { /* handle error two */  }
>> catch   { /* handle possible new errors in later versions of the 
>> library */ }
>> 
>> - I usually have _semantic_ namespaces for Errors, rather than single types 
>> per implementation pattern. If we are adding strong annotations about which 
>> errors can be thrown, I’d quite like to incorporate that pattern. For 
>> example:
>> 
>> extension File {
>> @fixed enum OpeningError { 
>>  case .invalidPath
>>  case .accessDenied  // e.g. asking for write permissions for read-only 
>> file
>> }
>> @fixed enum ReadError {
>>  case .invalidOffset // past EOF
>>  case .deviceError   // probably worth aborting the entire operation the 
>> read is part of
>> }
>> 
>> // - throws:
>> // - .OpeningError 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-20 Thread John McCall via swift-evolution
> On Feb 19, 2017, at 3:04 PM, Anton Zhilin via swift-evolution 
>  wrote:
> It’s expected that if you need resilience, then you will throw an “open” 
> enum. Essentially, we pass resilience of typed throws on to those who will 
> hopefully establish resilience of enums.
> 
> If you prefer separate error types, then declare a base protocol for all your 
> error types and throw a protocol existential. You won’t even need default 
> case in switches, if closed protocols make it into the language.
> 
> I don’t like any solution that is based on comments. I think that compiler 
> should always ignore comments.
> 
I agree.  And in general, this sort of thing is exactly my core concern about 
adding typed throws to the language: I am completely certain that many 
programmers will add typed throws annotations because they're programmers and 
thus, well, probably a little obsessive/compulsive, and they're trying to 
precisely document the behavior of their function without necessarily thinking 
about the usefulness of that information for their clients and (if they're 
writing a library; and really you should almost always be writing code as if 
you're writing a library) whether they're actually willing to commit to that 
behavior in their interface.  For those programmers, typed throws is just going 
to box them in and force them into anti-patterns in the long run.

In the vast majority of use-cases, clients are not going to exhaustively handle 
all errors — they will always have some generic fall-back.  That is not 
pessimism, it's actually the natural result of the complicated world we live 
in, where code can fail for a huge host of reasons and most callers won't have 
meaningful special-case behavior for all of them.  (On most operating systems, 
opening a file or a network connection can fail because you ran out of file 
descriptors.  You're seriously telling me that you're going to add a special 
case to your error logic for that?)  Go look at the actual error types that 
people use in most typed-throws situations and try to tell me I'm wrong — they 
probably have like twenty alternatives, and solidly a quarter or more of them 
will just be embedding some other arbitrarily-complex or stringly-typed error 
value.

The real use case for typed throws is when you have something like a parser 
library that really does only fail in a fixed number of semantically distinct 
ways, and you both (1) actually care about enforcing that in the implementation 
and making sure that other errors are handled internally and (2) you really do 
expect clients to exhaustively switch over the error at some point.  That's 
important.  But I continue to think that if adding better support for that use 
case misleads other programmers into thinking they should use typed throws, we 
will have made the language worse overall.

John.



> 2017-02-18 18:27 GMT+03:00 Karl Wagner  >:
> 
> 
> 
> 
> So, I’m not sure about what was decided last time, but my issues with this 
> are:
> 
> - The thrown error type will become part of the ABI of the function. If you 
> change the type of Error that is thrown, callers may not catch it. At the 
> same time, if we make enums resilient by default and only allow specifying a 
> single entire type, you will basically need one Error enum per function and 
> it will need to be @fixed if you actually want to remove the catch-all block. 
> Otherwise:
> 
> // Let’s say this isn’t @fixed...
> enum CanFailError {
> errorOne
> errorTwo
> }
> 
> func canFail() throws(CanFailError) { /* … */ }
> 
> do { try canFail() }
> catch CanFailError {
> switch error {
> case .errorOne: /* handle error one */
> case .errorTwo: /* handle error two */
> default:/* handle possible new errors in later versions of 
> the library */
> }
> }
> 
> do { try canFail() }
> catch .errorOne { /* handle error one */ }
> catch .errorTwo { /* handle error two */  }
> catch   { /* handle possible new errors in later versions of the 
> library */ }
> 
> - I usually have _semantic_ namespaces for Errors, rather than single types 
> per implementation pattern. If we are adding strong annotations about which 
> errors can be thrown, I’d quite like to incorporate that pattern. For example:
> 
> extension File {
> @fixed enum OpeningError { 
>   case .invalidPath
>   case .accessDenied  // e.g. asking for write permissions for read-only 
> file
> }
> @fixed enum ReadError {
>   case .invalidOffset // past EOF
>   case .deviceError   // probably worth aborting the entire operation the 
> read is part of
> }
> 
> // - throws:
> // - .OpeningError if the file can’t be opened
> // - .ReadError if the read operation fails
> func read(from offset: Int, into buffer: UnsafeBufferPointer) 
> throws(OpeningError, ReadError) { /* … */ }
> }
> 
> - I wonder if we could try something more ambitious. 

Re: [swift-evolution] [Pitch] Typed throws

2017-02-19 Thread Anton Zhilin via swift-evolution
It’s expected that if you need resilience, then you will throw an “open”
enum. Essentially, we pass resilience of typed throws on to those who will
hopefully establish resilience of enums.

If you prefer separate error types, then declare a base protocol for all
your error types and throw a protocol existential. You won’t even need
default case in switches, if closed protocols make it into the language.

I don’t like any solution that is based on comments. I think that compiler
should always ignore comments.

2017-02-18 18:27 GMT+03:00 Karl Wagner :


> So, I’m not sure about what was decided last time, but my issues with this
> are:
>
> - The thrown error type will become part of the ABI of the function. If
> you change the type of Error that is thrown, callers may not catch it. At
> the same time, if we make enums resilient by default and only allow
> specifying a single entire type, you will basically need one Error enum per
> function and it will need to be @fixed if you actually want to remove the
> catch-all block. Otherwise:
>
> // Let’s say this isn’t @fixed...
>
> enum CanFailError {
> errorOne
> errorTwo
> }
>
> func canFail() throws(CanFailError) { /* … */ }
>
> do { try canFail() }
> catch CanFailError {
> switch error {
> case .errorOne: /* handle error one */
> case .errorTwo: /* handle error two */
> default:/* handle possible new errors in later versions of
> the library */
> }
> }
>
> do { try canFail() }
> catch .errorOne { /* handle error one */ }
> catch .errorTwo { /* handle error two */  }
> catch   { /* handle possible new errors in later versions of the
> library */ }
>
> - I usually have _semantic_ namespaces for Errors, rather than single
> types per implementation pattern. If we are adding strong annotations about
> which errors can be thrown, I’d quite like to incorporate that pattern. For
> example:
>
> extension File {
>
> @fixed enum OpeningError {
>
> case .invalidPath
> case .accessDenied  // e.g. asking for write permissions for read-only file
>
> }
> @fixed enum ReadError {
>
> case .invalidOffset // past EOF
> case .deviceError   // probably worth aborting the entire operation the
> read is part of
>
> }
>
> // - throws:
> // - .OpeningError if the file can’t be opened
> // - .ReadError if the read operation fails
> func read(from offset: Int, into buffer: UnsafeBufferPointer)
> throws(OpeningError, ReadError) { /* … */ }
>
> }
>
>
> - I wonder if we could try something more ambitious. Since the list of
> thrown errors is resilience-breaking for the function, it is only
> beneficial for versioned and @inlineable functions. They should not be able
> to add new errors (they can remove them though, since errors are intended
> to be switched over). I wonder if we couldn’t introduce a small pattern
> grammar for our structured comments (isolated from the rest of the
> language) - it would be optional, but if you do list your errors, the
> compiler would validate that you do it exhaustively. Some patterns I would
> like are:
>
> // - throws: - MyError.{errorOne, errorThree, errorFive}: Something bad
>|| considered exhaustive
> @inlineable public func canFail() throws {}
>
> // - throws: - OpeningError: Computer says no... || considered
> exhaustive if OpeningError is versioned or @fixed
> //   - * || other errors,
> requires “catch-all” by external callers
> @inlineable public func canFail2() throws {}
>
> If we want to get really clever, we can have the compiler automatically
> generate those error-lists for internal functions, so you would
> automatically get exhaustive error-handling within your own module.
>
> - Karl
>
​
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-19 Thread Matthew Johnson via swift-evolution


Sent from my iPad

> On Feb 19, 2017, at 11:34 AM, Brandon Knope via swift-evolution 
>  wrote:
> 
> I really like this. Seems much more elegant and simple this way 

+1

> 
>> On Feb 17, 2017, at 4:45 PM, Joe Groff via swift-evolution 
>>  wrote:
>> 
>> 
>>> On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution 
>>>  wrote:
>>> 
>>> I suggest we need to find a way to shorten the list of the possible error 
>>> types with a the help of typeallias
>>> 
>>> extension MyError1: Error { ... }
>>> extension MyError2: Error { ... }
>>> extension MyError3: Error { ... }
>>> 
>>> typealias MyErrors = MyError1 | MyError2 | MyError3  
>>> 
>>> func foo() throws(MyErrors) -> MyResult
>>> func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> 
>>> MyResult
>> Do you actually need that? Experience in other languages like Rust and 
>> Haskell that use Result-based error propagation suggests that a single error 
>> type is adequate, and beneficial in many ways. If nothing else, you could 
>> `Either` your way to multiple errors if you really needed to.
>> 
>> IMO, if we accept a single error type per function, there could be a simpler 
>> model for this. We could say that the `throws` type is a generic parameter 
>> of all function types, and it defaults to the uninhabited `Never` type for 
>> functions that don't throw.
>> 
>> () -> () == () throws Never -> ()
>> () throws -> () == () throws Error -> ()
>> 
>> In this model, you'd get many benefits:
>> 
>> - `rethrows` could become first-class, reducing down to just polymorphic 
>> `throws`:
>> 
>> func foo(_: () throws -> ()) rethrows // Swift 3
>> func foo(_: () throws T -> ()) throws T // Swift X
>> func foo(_: () throws T -> ()) throws Either
>> 
>> - Protocols could abstract over error handling; for instance, we could 
>> support throwing sequences:
>> 
>> protocol IteratorProtocol {
>>   associatedtype Element
>>   associatedtype Error: Swift.Error = Never
>> 
>>   mutating func next() throws Error -> Element?
>> }
>> 
>> Separate of the type system model, the type *checking* model also deserves 
>> thorough consideration. Propagating the effects of possibly multiple error 
>> types propagating within a `do` block is much trickier than doing so as a 
>> single "throws" or not bit, especially if you want to be able to use type 
>> context in `catch` patterns or to implicitly propagate a narrower `throws` 
>> type out of the enclosing function.
>> 
>> -Joe
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-19 Thread Brandon Knope via swift-evolution
I really like this. Seems much more elegant and simple this way 

> On Feb 17, 2017, at 4:45 PM, Joe Groff via swift-evolution 
>  wrote:
> 
> 
>> On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution 
>>  wrote:
>> 
>> I suggest we need to find a way to shorten the list of the possible error 
>> types with a the help of typeallias
>> 
>> extension MyError1: Error { ... }
>> extension MyError2: Error { ... }
>> extension MyError3: Error { ... }
>> 
>> typealias MyErrors = MyError1 | MyError2 | MyError3  
>> 
>> func foo() throws(MyErrors) -> MyResult
>> func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> 
>> MyResult
> Do you actually need that? Experience in other languages like Rust and 
> Haskell that use Result-based error propagation suggests that a single error 
> type is adequate, and beneficial in many ways. If nothing else, you could 
> `Either` your way to multiple errors if you really needed to.
> 
> IMO, if we accept a single error type per function, there could be a simpler 
> model for this. We could say that the `throws` type is a generic parameter of 
> all function types, and it defaults to the uninhabited `Never` type for 
> functions that don't throw.
> 
> () -> () == () throws Never -> ()
> () throws -> () == () throws Error -> ()
> 
> In this model, you'd get many benefits:
> 
> - `rethrows` could become first-class, reducing down to just polymorphic 
> `throws`:
> 
> func foo(_: () throws -> ()) rethrows // Swift 3
> func foo(_: () throws T -> ()) throws T // Swift X
> func foo(_: () throws T -> ()) throws Either
> 
> - Protocols could abstract over error handling; for instance, we could 
> support throwing sequences:
> 
> protocol IteratorProtocol {
>   associatedtype Element
>   associatedtype Error: Swift.Error = Never
> 
>   mutating func next() throws Error -> Element?
> }
> 
> Separate of the type system model, the type *checking* model also deserves 
> thorough consideration. Propagating the effects of possibly multiple error 
> types propagating within a `do` block is much trickier than doing so as a 
> single "throws" or not bit, especially if you want to be able to use type 
> context in `catch` patterns or to implicitly propagate a narrower `throws` 
> type out of the enclosing function.
> 
> -Joe
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-18 Thread Karl Wagner via swift-evolution

> On 18 Feb 2017, at 16:27, Karl Wagner  wrote:
> 
> - I wonder if we could try something more ambitious. Since the list of thrown 
> errors is resilience-breaking for the function, it is only beneficial for 
> versioned and @inlineable functions. They should not be able to add new 
> errors (they can remove them though, since errors are intended to be switched 
> over). I wonder if we couldn’t introduce a small pattern grammar for our 
> structured comments (isolated from the rest of the language) - it would be 
> optional, but if you do list your errors, the compiler would validate that 
> you do it exhaustively. Some patterns I would like are:
> 
> // - throws: - MyError.{errorOne, errorThree, errorFive}: Something bad  
> || considered exhaustive
> @inlineable public func canFail() throws {}
> 
> // - throws: - OpeningError: Computer says no... || considered 
> exhaustive if OpeningError is versioned or @fixed
> //   - * || other errors, 
> requires “catch-all” by external callers
> @inlineable public func canFail2() throws {}
> 
> If we want to get really clever, we can have the compiler automatically 
> generate those error-lists for internal functions, so you would automatically 
> get exhaustive error-handling within your own module.
> 
> - Karl

Just to rephrase this a little bit.

- The list of thrown errors would not be part of the function’s signature. 
Instead, there is an ABI invariant that it throws an Error, and that it remains 
"switch-compatible". That’s a loose definition, but it covers things like 
throwing non-public Errors and later making them public while retaining ABI 
compatibility.
- It’s up to the library developer to maintain that invariant. They can remove 
errors, refine errors (e.g. “MyError” -> “MyError.{errorOne, errorThree}”), 
hide or reveal previously-hidden errors.
- If the errors are listed exhaustively, the compiler will generate an implicit 
catch-all to trap in case the library violates this invariant. This means they 
can become more specific while retaining ABI compatibility.

- We would put this information in the structured comments, because it can get 
long and complex and is always optional
- The compiler would ensure that their documentation is accurate, as it does 
any other part of the language.

- Karl___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-18 Thread Karl Wagner via swift-evolution

> On 17 Feb 2017, at 19:45, Anton Zhilin via swift-evolution 
>  wrote:
> 
> Now this is on-topic, I guess.
> Last time we stopped at John McCall’s syntax:
> 
> extension MyError: Error { ... }
> 
> func foo() throws(MyError) -> MyResult
> It’s conservative and prevents visual ambiguity with extra parentheses.
> 
> If we (somewhat) agree on this, then submitting a proposal will be trivial.
> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


So, I’m not sure about what was decided last time, but my issues with this are:

- The thrown error type will become part of the ABI of the function. If you 
change the type of Error that is thrown, callers may not catch it. At the same 
time, if we make enums resilient by default and only allow specifying a single 
entire type, you will basically need one Error enum per function and it will 
need to be @fixed if you actually want to remove the catch-all block. Otherwise:

// Let’s say this isn’t @fixed...
enum CanFailError {
errorOne
errorTwo
}

func canFail() throws(CanFailError) { /* … */ }

do { try canFail() }
catch CanFailError {
switch error {
case .errorOne: /* handle error one */
case .errorTwo: /* handle error two */
default:/* handle possible new errors in later versions of the 
library */
}
}

do { try canFail() }
catch .errorOne { /* handle error one */ }
catch .errorTwo { /* handle error two */  }
catch   { /* handle possible new errors in later versions of the 
library */ }

- I usually have _semantic_ namespaces for Errors, rather than single types per 
implementation pattern. If we are adding strong annotations about which errors 
can be thrown, I’d quite like to incorporate that pattern. For example:

extension File {
@fixed enum OpeningError { 
case .invalidPath
case .accessDenied  // e.g. asking for write permissions for read-only 
file
}
@fixed enum ReadError {
case .invalidOffset // past EOF
case .deviceError   // probably worth aborting the entire operation the 
read is part of
}

// - throws:
// - .OpeningError if the file can’t be opened
// - .ReadError if the read operation fails
func read(from offset: Int, into buffer: UnsafeBufferPointer) 
throws(OpeningError, ReadError) { /* … */ }
}

- I wonder if we could try something more ambitious. Since the list of thrown 
errors is resilience-breaking for the function, it is only beneficial for 
versioned and @inlineable functions. They should not be able to add new errors 
(they can remove them though, since errors are intended to be switched over). I 
wonder if we couldn’t introduce a small pattern grammar for our structured 
comments (isolated from the rest of the language) - it would be optional, but 
if you do list your errors, the compiler would validate that you do it 
exhaustively. Some patterns I would like are:

// - throws: - MyError.{errorOne, errorThree, errorFive}: Something bad  || 
considered exhaustive
@inlineable public func canFail() throws {}

// - throws: - OpeningError: Computer says no... || considered 
exhaustive if OpeningError is versioned or @fixed
//   - * || other errors, 
requires “catch-all” by external callers
@inlineable public func canFail2() throws {}

If we want to get really clever, we can have the compiler automatically 
generate those error-lists for internal functions, so you would automatically 
get exhaustive error-handling within your own module.

- Karl___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-18 Thread Tino Heth via swift-evolution

> Wouldn’t that mean that you couldn’t use your Swift library in Objective-C 
> anymore, at least the error type as an NSError?
> 
That's the meaning of "break with Objective-C" here ;-) — but note that I wrote 
about allowing to do so, not forcing:
Now, we can only declare that something is thrown, and as interoperability is 
quite important, we have to assume it is needed.
As soon as you declare exactly what will be thrown, it should be up to you to 
decide if you need NSError-bridging.___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Colin Barrett via swift-evolution
On Fri, Feb 17, 2017 at 4:46 PM Joe Groff via swift-evolution <
swift-evolution@swift.org> wrote:

> On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> I suggest we need to find a way to shorten the list of the possible error
> types with a the help of typeallias
>
> extension MyError1: Error { ... }
> extension MyError2: Error { ... }
> extension MyError3: Error { ... }
>
> typealias MyErrors = MyError1 | MyError2 | MyError3
>
> func foo() throws(MyErrors) -> MyResult
> func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> MyResult
>
> Do you actually need that? Experience in other languages like Rust and
> Haskell that use Result-based error propagation suggests that a single
> error type is adequate, and beneficial in many ways. If nothing else, you
> could `Either` your way to multiple errors if you really needed to.
>
> IMO, if we accept a single error type per function, there could be a
> simpler model for this. We could say that the `throws` type is a generic
> parameter of all function types, and it defaults to the uninhabited `Never`
> type for functions that don't throw.
>
> () -> () == () throws Never -> ()
> () throws -> () == () throws Error -> ()
>
> In this model, you'd get many benefits:
>
> - `rethrows` could become first-class, reducing down to just polymorphic
> `throws`:
>
> func foo(_: () throws -> ()) rethrows // Swift 3
> func foo(_: () throws T -> ()) throws T // Swift X
> func foo(_: () throws T -> ()) throws Either
>
> - Protocols could abstract over error handling; for instance, we could
> support throwing sequences:
>
> protocol IteratorProtocol {
>   associatedtype Element
>   associatedtype Error: Swift.Error = Never
>
>   mutating func next() throws Error -> Element?
> }
>
>
I really dig this proposal, Joe. Matches my experience from other languages
as well.

-Colin
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Daniel Leping via swift-evolution
Here we go again... -1

On Fri, 17 Feb 2017 at 23:52 Matthew Johnson via swift-evolution <
swift-evolution@swift.org> wrote:

> On Feb 17, 2017, at 3:45 PM, Joe Groff via swift-evolution <
> swift-evolution@swift.org> wrote:
>
>
> On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> I suggest we need to find a way to shorten the list of the possible error
> types with a the help of typeallias
>
> extension MyError1: Error { ... }
> extension MyError2: Error { ... }
> extension MyError3: Error { ... }
>
> typealias MyErrors = MyError1 | MyError2 | MyError3
>
> func foo() throws(MyErrors) -> MyResult
> func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> MyResult
>
> Do you actually need that? Experience in other languages like Rust and
> Haskell that use Result-based error propagation suggests that a single
> error type is adequate, and beneficial in many ways. If nothing else, you
> could `Either` your way to multiple errors if you really needed to.
>
> IMO, if we accept a single error type per function, there could be a
> simpler model for this. We could say that the `throws` type is a generic
> parameter of all function types, and it defaults to the uninhabited `Never`
> type for functions that don't throw.
>
> () -> () == () throws Never -> ()
> () throws -> () == () throws Error -> ()
>
> In this model, you'd get many benefits:
>
> - `rethrows` could become first-class, reducing down to just polymorphic
> `throws`:
>
> func foo(_: () throws -> ()) rethrows // Swift 3
> func foo(_: () throws T -> ()) throws T // Swift X
> func foo(_: () throws T -> ()) throws Either
>
> - Protocols could abstract over error handling; for instance, we could
> support throwing sequences:
>
> protocol IteratorProtocol {
>   associatedtype Element
>   associatedtype Error: Swift.Error = Never
>
>   mutating func next() throws Error -> Element?
> }
>
> Separate of the type system model, the type *checking* model also deserves
> thorough consideration. Propagating the effects of possibly multiple error
> types propagating within a `do` block is much trickier than doing so as a
> single "throws" or not bit, especially if you want to be able to use type
> context in `catch` patterns or to implicitly propagate a narrower `throws`
> type out of the enclosing function.
>
>
> I agree with all of this. The correct way to handle functions that throw
> multiple error types is with a discriminated union (enum) or an
> existential.
>
> If we make enums easier to work with (see my manifesto on generalized
> enums and value subtyping), we also make it easier to address the use case
> of functions that need to throw several different error types.  That is the
> more general and useful way to address this use case.
>
>
> -Joe
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Matthew Johnson via swift-evolution

> On Feb 17, 2017, at 3:45 PM, Joe Groff via swift-evolution 
>  wrote:
> 
> 
>> On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution 
>> > wrote:
>> 
>> I suggest we need to find a way to shorten the list of the possible error 
>> types with a the help of typeallias
>> 
>> extension MyError1: Error { ... }
>> extension MyError2: Error { ... }
>> extension MyError3: Error { ... }
>> 
>> typealias MyErrors = MyError1 | MyError2 | MyError3  
>> 
>> func foo() throws(MyErrors) -> MyResult
>> func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> 
>> MyResult
> Do you actually need that? Experience in other languages like Rust and 
> Haskell that use Result-based error propagation suggests that a single error 
> type is adequate, and beneficial in many ways. If nothing else, you could 
> `Either` your way to multiple errors if you really needed to.
> 
> IMO, if we accept a single error type per function, there could be a simpler 
> model for this. We could say that the `throws` type is a generic parameter of 
> all function types, and it defaults to the uninhabited `Never` type for 
> functions that don't throw.
> 
> () -> () == () throws Never -> ()
> () throws -> () == () throws Error -> ()
> 
> In this model, you'd get many benefits:
> 
> - `rethrows` could become first-class, reducing down to just polymorphic 
> `throws`:
> 
> func foo(_: () throws -> ()) rethrows // Swift 3
> func foo(_: () throws T -> ()) throws T // Swift X
> func foo(_: () throws T -> ()) throws Either
> 
> - Protocols could abstract over error handling; for instance, we could 
> support throwing sequences:
> 
> protocol IteratorProtocol {
>   associatedtype Element
>   associatedtype Error: Swift.Error = Never
> 
>   mutating func next() throws Error -> Element?
> }
> 
> Separate of the type system model, the type *checking* model also deserves 
> thorough consideration. Propagating the effects of possibly multiple error 
> types propagating within a `do` block is much trickier than doing so as a 
> single "throws" or not bit, especially if you want to be able to use type 
> context in `catch` patterns or to implicitly propagate a narrower `throws` 
> type out of the enclosing function.

I agree with all of this. The correct way to handle functions that throw 
multiple error types is with a discriminated union (enum) or an existential.  

If we make enums easier to work with (see my manifesto on generalized enums and 
value subtyping), we also make it easier to address the use case of functions 
that need to throw several different error types.  That is the more general and 
useful way to address this use case.

> 
> -Joe
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
I meant to say “… more than one throwing error type …”



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 22:48:35, Adrian Zubarev 
(adrian.zuba...@devandartist.com) schrieb:

No actually not, it was made up by the assumption that the proposed syntax 
would have more than one throwing that which was clarified by others to be 
incorrect. ;)

Thanks again for clarification.



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 22:45:35, Joe Groff (jgr...@apple.com) schrieb:


On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution 
 wrote:

I suggest we need to find a way to shorten the list of the possible error types 
with a the help of typeallias

extension MyError1: Error { ... }
extension MyError2: Error { ... }
extension MyError3: Error { ... }

typealias MyErrors = MyError1 | MyError2 | MyError3

func foo() throws(MyErrors) -> MyResult
func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> MyResult
Do you actually need that? Experience in other languages like Rust and Haskell 
that use Result-based error propagation suggests that a single error type is 
adequate, and beneficial in many ways. If nothing else, you could `Either` your 
way to multiple errors if you really needed to.

IMO, if we accept a single error type per function, there could be a simpler 
model for this. We could say that the `throws` type is a generic parameter of 
all function types, and it defaults to the uninhabited `Never` type for 
functions that don't throw.

() -> () == () throws Never -> ()
() throws -> () == () throws Error -> ()

In this model, you'd get many benefits:

- `rethrows` could become first-class, reducing down to just polymorphic 
`throws`:

func foo(_: () throws -> ()) rethrows // Swift 3
func foo(_: () throws T -> ()) throws T // Swift X
func foo(_: () throws T -> ()) throws Either

- Protocols could abstract over error handling; for instance, we could support 
throwing sequences:

protocol IteratorProtocol {
  associatedtype Element
  associatedtype Error: Swift.Error = Never

  mutating func next() throws Error -> Element?
}

Separate of the type system model, the type *checking* model also deserves 
thorough consideration. Propagating the effects of possibly multiple error 
types propagating within a `do` block is much trickier than doing so as a 
single "throws" or not bit, especially if you want to be able to use type 
context in `catch` patterns or to implicitly propagate a narrower `throws` type 
out of the enclosing function.

-Joe
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
No actually not, it was made up by the assumption that the proposed syntax 
would have more than one throwing that which was clarified by others to be 
incorrect. ;)

Thanks again for clarification.



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 22:45:35, Joe Groff (jgr...@apple.com) schrieb:


On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution 
 wrote:

I suggest we need to find a way to shorten the list of the possible error types 
with a the help of typeallias

extension MyError1: Error { ... }
extension MyError2: Error { ... }
extension MyError3: Error { ... }

typealias MyErrors = MyError1 | MyError2 | MyError3   

func foo() throws(MyErrors) -> MyResult
func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> MyResult
Do you actually need that? Experience in other languages like Rust and Haskell 
that use Result-based error propagation suggests that a single error type is 
adequate, and beneficial in many ways. If nothing else, you could `Either` your 
way to multiple errors if you really needed to.

IMO, if we accept a single error type per function, there could be a simpler 
model for this. We could say that the `throws` type is a generic parameter of 
all function types, and it defaults to the uninhabited `Never` type for 
functions that don't throw.

() -> () == () throws Never -> ()
() throws -> () == () throws Error -> ()

In this model, you'd get many benefits:

- `rethrows` could become first-class, reducing down to just polymorphic 
`throws`:

func foo(_: () throws -> ()) rethrows // Swift 3
func foo(_: () throws T -> ()) throws T // Swift X
func foo(_: () throws T -> ()) throws Either

- Protocols could abstract over error handling; for instance, we could support 
throwing sequences:

protocol IteratorProtocol {
  associatedtype Element
  associatedtype Error: Swift.Error = Never

  mutating func next() throws Error -> Element?
}

Separate of the type system model, the type *checking* model also deserves 
thorough consideration. Propagating the effects of possibly multiple error 
types propagating within a `do` block is much trickier than doing so as a 
single "throws" or not bit, especially if you want to be able to use type 
context in `catch` patterns or to implicitly propagate a narrower `throws` type 
out of the enclosing function.

-Joe
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Joe Groff via swift-evolution

> On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution 
>  wrote:
> 
> I suggest we need to find a way to shorten the list of the possible error 
> types with a the help of typeallias
> 
> extension MyError1: Error { ... }
> extension MyError2: Error { ... }
> extension MyError3: Error { ... }
> 
> typealias MyErrors = MyError1 | MyError2 | MyError3  
> 
> func foo() throws(MyErrors) -> MyResult
> func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> MyResult
Do you actually need that? Experience in other languages like Rust and Haskell 
that use Result-based error propagation suggests that a single error type is 
adequate, and beneficial in many ways. If nothing else, you could `Either` your 
way to multiple errors if you really needed to.

IMO, if we accept a single error type per function, there could be a simpler 
model for this. We could say that the `throws` type is a generic parameter of 
all function types, and it defaults to the uninhabited `Never` type for 
functions that don't throw.

() -> () == () throws Never -> ()
() throws -> () == () throws Error -> ()

In this model, you'd get many benefits:

- `rethrows` could become first-class, reducing down to just polymorphic 
`throws`:

func foo(_: () throws -> ()) rethrows // Swift 3
func foo(_: () throws T -> ()) throws T // Swift X
func foo(_: () throws T -> ()) throws Either

- Protocols could abstract over error handling; for instance, we could support 
throwing sequences:

protocol IteratorProtocol {
  associatedtype Element
  associatedtype Error: Swift.Error = Never

  mutating func next() throws Error -> Element?
}

Separate of the type system model, the type *checking* model also deserves 
thorough consideration. Propagating the effects of possibly multiple error 
types propagating within a `do` block is much trickier than doing so as a 
single "throws" or not bit, especially if you want to be able to use type 
context in `catch` patterns or to implicitly propagate a narrower `throws` type 
out of the enclosing function.

-Joe
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Anton Zhilin via swift-evolution
Several proposals will follow this one: allowing multiple error types,
removing Error, replacing rethrows, etc.
Those topics are more controversial, but fortunately for them, they mostly
add on top of the core feature being discussed.
So IMO, if a detail can be split into its own proposal, we should just do
it and forget about it for a little while.

2017-02-17 23:16 GMT+03:00 Tino Heth <2...@gmx.de>:


> I thought it was going to be any one subtype of Error, be it a struct, an
> enum, or a protocol existential, or Error itself.
>
>
> Imho we should remove the restriction that you can only throw
> Error-conforming types if typed throws are added:
> It's a compatibility feature, and if you manually declare what can be
> thrown, you should be allowed to break with Objective-C.
>
> As Error has no requirements at all, it looks like harassment, as its
> whole magic is neither visible nor necessary.
>
> ​
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
Wouldn’t that mean that you couldn’t use your Swift library in Objective-C 
anymore, at least the error type as an NSError?



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 21:16:57, Tino Heth (2...@gmx.de) schrieb:


I thought it was going to be any one subtype of Error, be it a struct, an enum, 
or a protocol existential, or Error itself.

Imho we should remove the restriction that you can only throw Error-conforming 
types if typed throws are added:
It's a compatibility feature, and if you manually declare what can be thrown, 
you should be allowed to break with Objective-C.

As Error has no requirements at all, it looks like harassment, as its whole 
magic is neither visible nor necessary.

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
I disagree with that, it works if you only have a single function parameter 
type that throws an error, but if there are more than one inferring the type 
won’t be possible anymore: func foo(_: () throws(T) -> Void, _: () throws(S) -> 
Void) rethrows(S) (here, we’re assuming that T is handled inside foo).



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 21:17:42, Kevin Nattinger (sw...@nattinger.net) schrieb:


On Feb 17, 2017, at 11:50 AM, Adrian Zubarev via swift-evolution 
 wrote:

Sorry, I couldn’t follow every thread. I simply couldn’t get that fact from the 
given context of the first post by Anton. :) Just forget everything I mentioned 
about typealias, because it was based on the assumption of an error list.

Anyways +1 for typed throws. The syntax throws(T) and rethrows(T) is fine by me.

I feel like  rethrows can and should be inferred. It’s just a pass-through.




-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:45:55, Matthew Johnson (matt...@anandabits.com) 
schrieb:


On Feb 17, 2017, at 1:42 PM, Adrian Zubarev via swift-evolution 
 wrote:

So the typed throws are going to be limited to a single error type, is that the 
direction we're heading to?

Yes, this topic was discussed thoroughly last year.


-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:39:12, Anton Zhilin (antonyzhi...@gmail.com) schrieb:

In this case, you’d better create a new error type, which includes all the 
cases of those errors:

// FileNotFoundError and WrongFormat are Error-s

struct PreferencesError : Error {
init(_: FileNotFoundError)
init(_: WrongFormat)
// ...
}

func readPreferences() throws(PreferencesError)
In the most “lazy” case, you’d just create an enum of those two:

enum PreferencesError : Error {
case fileNotFound(FileNotFoundError)
case wrongFormat(WrongFormatError)
}
Better yet, you should analyze, which cases are meaningful for user of 
readPreferences, and present them with appropriate interface. You may want to 
crash on those cases of initial error types, with which you can’t do anything 
on the level of abstraction of readPreferences. Some of the others will be 
merged or renamed.

With proper error types, a single type in throws clause is enough, without sum 
types.

2017-02-17 22:22 GMT+03:00 Adrian Zubarev via swift-evolution 
:


Sure thing, but that’s not what I was asking about. Kevin made a protocol that 
conforms to Error where all the his enums conformed to MyError protocol. That 
way we’re losing all enum cases and are not really a step further as before.


___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Kevin Nattinger via swift-evolution

> On Feb 17, 2017, at 11:50 AM, Adrian Zubarev via swift-evolution 
>  wrote:
> 
> Sorry, I couldn’t follow every thread. I simply couldn’t get that fact from 
> the given context of the first post by Anton. :) Just forget everything I 
> mentioned about typealias, because it was based on the assumption of an error 
> list.
> 
> Anyways +1 for typed throws. The syntax throws(T) and rethrows(T) is fine by 
> me.
> 
I feel like rethrows can and should be inferred. It’s just a pass-through.

> 
> 
> 
> -- 
> Adrian Zubarev
> Sent with Airmail
> 
> Am 17. Februar 2017 um 20:45:55, Matthew Johnson (matt...@anandabits.com 
> ) schrieb:
> 
>> 
>>> On Feb 17, 2017, at 1:42 PM, Adrian Zubarev via swift-evolution 
>>> > wrote:
>>> 
>>> So the typed throws are going to be limited to a single error type, is that 
>>> the direction we're heading to?
>> 
>> Yes, this topic was discussed thoroughly last year.
>> 
>>> 
>>> -- 
>>> Adrian Zubarev
>>> Sent with Airmail
>>> 
>>> Am 17. Februar 2017 um 20:39:12, Anton Zhilin (antonyzhi...@gmail.com 
>>> ) schrieb:
>>> 
 In this case, you’d better create a new error type, which includes all the 
 cases of those errors:
 
 // FileNotFoundError and WrongFormat are Error-s
 
 struct PreferencesError : Error {
 init(_: FileNotFoundError)
 init(_: WrongFormat)
 // ...
 }
 
 func readPreferences() throws(PreferencesError)
 In the most “lazy” case, you’d just create an enum of those two:
 
 enum PreferencesError : Error {
 case fileNotFound(FileNotFoundError)
 case wrongFormat(WrongFormatError)
 }
 Better yet, you should analyze, which cases are meaningful for user of 
 readPreferences, and present them with appropriate interface. You may want 
 to crash on those cases of initial error types, with which you can’t do 
 anything on the level of abstraction of readPreferences. Some of the 
 others will be merged or renamed.
 
 With proper error types, a single type in throws clause is enough, without 
 sum types.
 
 2017-02-17 22:22 GMT+03:00 Adrian Zubarev via swift-evolution 
 >:
 
 
 Sure thing, but that’s not what I was asking about. Kevin made a protocol 
 that conforms to Error where all the his enums conformed to MyError 
 protocol. That way we’re losing all enum cases and are not really a step 
 further as before.
 
 
>>> ___
>>> swift-evolution mailing list
>>> swift-evolution@swift.org 
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> 
> 
> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Tino Heth via swift-evolution

> I thought it was going to be any one subtype of Error, be it a struct, an 
> enum, or a protocol existential, or Error itself.

Imho we should remove the restriction that you can only throw Error-conforming 
types if typed throws are added:
It's a compatibility feature, and if you manually declare what can be thrown, 
you should be allowed to break with Objective-C.

As Error has no requirements at all, it looks like harassment, as its whole 
magic is neither visible nor necessary.

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
Sorry, I couldn’t follow every thread. I simply couldn’t get that fact from the 
given context of the first post by Anton. :) Just forget everything I mentioned 
about typealias, because it was based on the assumption of an error list.

Anyways +1 for typed throws. The syntax throws(T) and rethrows(T) is fine by me.



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:45:55, Matthew Johnson (matt...@anandabits.com) 
schrieb:


On Feb 17, 2017, at 1:42 PM, Adrian Zubarev via swift-evolution 
 wrote:

So the typed throws are going to be limited to a single error type, is that the 
direction we're heading to?

Yes, this topic was discussed thoroughly last year.


-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:39:12, Anton Zhilin (antonyzhi...@gmail.com) schrieb:

In this case, you’d better create a new error type, which includes all the 
cases of those errors:

// FileNotFoundError and WrongFormat are Error-s

struct PreferencesError : Error {
init(_: FileNotFoundError)
init(_: WrongFormat)
// ...
}

func readPreferences() throws(PreferencesError)
In the most “lazy” case, you’d just create an enum of those two:

enum PreferencesError : Error {
case fileNotFound(FileNotFoundError)
case wrongFormat(WrongFormatError)
}
Better yet, you should analyze, which cases are meaningful for user of 
readPreferences, and present them with appropriate interface. You may want to 
crash on those cases of initial error types, with which you can’t do anything 
on the level of abstraction of readPreferences. Some of the others will be 
merged or renamed.

With proper error types, a single type in throws clause is enough, without sum 
types.

2017-02-17 22:22 GMT+03:00 Adrian Zubarev via swift-evolution 
:


Sure thing, but that’s not what I was asking about. Kevin made a protocol that 
conforms to Error where all the his enums conformed to MyError protocol. That 
way we’re losing all enum cases and are not really a step further as before.


___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Matthew Johnson via swift-evolution

> On Feb 17, 2017, at 1:42 PM, Adrian Zubarev via swift-evolution 
>  wrote:
> 
> So the typed throws are going to be limited to a single error type, is that 
> the direction we're heading to?

Yes, this topic was discussed thoroughly last year.

> 
> -- 
> Adrian Zubarev
> Sent with Airmail
> 
> Am 17. Februar 2017 um 20:39:12, Anton Zhilin (antonyzhi...@gmail.com 
> ) schrieb:
> 
>> In this case, you’d better create a new error type, which includes all the 
>> cases of those errors:
>> 
>> // FileNotFoundError and WrongFormat are Error-s
>> 
>> struct PreferencesError : Error {
>> init(_: FileNotFoundError)
>> init(_: WrongFormat)
>> // ...
>> }
>> 
>> func readPreferences() throws(PreferencesError)
>> In the most “lazy” case, you’d just create an enum of those two:
>> 
>> enum PreferencesError : Error {
>> case fileNotFound(FileNotFoundError)
>> case wrongFormat(WrongFormatError)
>> }
>> Better yet, you should analyze, which cases are meaningful for user of 
>> readPreferences, and present them with appropriate interface. You may want 
>> to crash on those cases of initial error types, with which you can’t do 
>> anything on the level of abstraction of 
>> readPreferences. Some of the others will be merged or renamed.
>> 
>> With proper error types, a single type in 
>> throws clause is enough, without sum types.
>> 
>> 2017-02-17 22:22 GMT+03:00 Adrian Zubarev via swift-evolution 
>> >:
>> 
>> 
>> Sure thing, but that’s not what I was asking about. Kevin made a protocol 
>> that conforms to Error where all the his enums conformed to MyError 
>> protocol. That way we’re losing all enum cases and are not really a step 
>> further as before.
>> 
>> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org 
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> 
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
So the typed throws are going to be limited to a single error type, is that the 
direction we're heading to?

-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:39:12, Anton Zhilin (antonyzhi...@gmail.com) schrieb:

In this case, you’d better create a new error type, which includes all the 
cases of those errors:

// FileNotFoundError and WrongFormat are Error-s

struct PreferencesError : Error {
init(_: FileNotFoundError)
init(_: WrongFormat)
// ...
}

func readPreferences() throws(PreferencesError)
In the most “lazy” case, you’d just create an enum of those two:

enum PreferencesError : Error {
case fileNotFound(FileNotFoundError)
case wrongFormat(WrongFormatError)
}
Better yet, you should analyze, which cases are meaningful for user of  
readPreferences, and present them with appropriate interface. You may want to 
crash on those cases of initial error types, with which you can’t do anything 
on the level of abstraction of  
readPreferences. Some of the others will be merged or renamed.

With proper error types, a single type in  
throws clause is enough, without sum types.

2017-02-17 22:22 GMT+03:00 Adrian Zubarev via swift-evolution 
:

Sure thing, but that’s not what I was asking about. Kevin made a protocol that 
conforms to Error where all the his enums conformed to MyError protocol. That 
way we’re losing all enum cases and are not really a step further as before.

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Anton Zhilin via swift-evolution
In this case, you’d better create a new error type, which includes all the
cases of those errors:

// FileNotFoundError and WrongFormat are Error-s

struct PreferencesError : Error {
init(_: FileNotFoundError)
init(_: WrongFormat)
// ...
}

func readPreferences() throws(PreferencesError)

In the most “lazy” case, you’d just create an enum of those two:

enum PreferencesError : Error {
case fileNotFound(FileNotFoundError)
case wrongFormat(WrongFormatError)
}

Better yet, you should analyze, which cases are meaningful for user of
readPreferences, and present them with appropriate interface. You may want
to crash on those cases of initial error types, with which you can’t do
anything on the level of abstraction of readPreferences. Some of the others
will be merged or renamed.

With proper error types, a single type in throws clause is enough, without
sum types.

2017-02-17 22:22 GMT+03:00 Adrian Zubarev via swift-evolution <
swift-evolution@swift.org>:

Sure thing, but that’s not what I was asking about. Kevin made a protocol
> that conforms to Error where all the his enums conformed to MyError
> protocol. That way we’re losing all enum cases and are not really a step
> further as before.
>
​
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
Sorry I meant “recover from MyError (protocol)”.



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:30:35, Adrian Zubarev 
(adrian.zuba...@devandartist.com) schrieb:

The function that throws MyError might contain your error enum (or other types 
in general), but you would have to recover from MyType to the dynamic type 
first.



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:26:48, Xiaodi Wu (xiaodi...@gmail.com) schrieb:

On Fri, Feb 17, 2017 at 1:22 PM, Adrian Zubarev via swift-evolution 
 wrote:
Sure thing, but that’s not what I was asking about. Kevin made a protocol that 
conforms to Error where all the his enums conformed to MyError protocol. That 
way we’re losing all enum cases and are not really a step further as before.


Sorry, can you elaborate? What do you mean by losing enum cases?

Am 17. Februar 2017 um 20:18:48, David Sweeris (daveswee...@mac.com) schrieb:


On Feb 17, 2017, at 11:12 AM, Adrian Zubarev via swift-evolution 
 wrote:

Is the throwing type always a protocol? In your example it is, but is this 
going to be always the case?

I think right now, there’s a bit of compiler magic in that you can only throw 
something that conforms to `Error`.

- Dave Sweeris

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
The function that throws MyError might contain your error enum (or other types 
in general), but you would have to recover from MyType to the dynamic type 
first.



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:26:48, Xiaodi Wu (xiaodi...@gmail.com) schrieb:

On Fri, Feb 17, 2017 at 1:22 PM, Adrian Zubarev via swift-evolution 
 wrote:
Sure thing, but that’s not what I was asking about. Kevin made a protocol that 
conforms to Error where all the his enums conformed to MyError protocol. That 
way we’re losing all enum cases and are not really a step further as before.


Sorry, can you elaborate? What do you mean by losing enum cases?

Am 17. Februar 2017 um 20:18:48, David Sweeris (daveswee...@mac.com) schrieb:


On Feb 17, 2017, at 11:12 AM, Adrian Zubarev via swift-evolution 
 wrote:

Is the throwing type always a protocol? In your example it is, but is this 
going to be always the case?

I think right now, there’s a bit of compiler magic in that you can only throw 
something that conforms to `Error`.

- Dave Sweeris

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Matthew Johnson via swift-evolution

> On Feb 17, 2017, at 1:24 PM, Xiaodi Wu via swift-evolution 
>  wrote:
> 
> Let's not bring bikeshedding the commonly proposed and rejected union 
> spelling into this.
> Typed throws would be a nice addition, assuming that the core team finds it 
> in scope for phase 2. It seems only logical that any type can be thrown (i.e. 
> conforms to Error) should be permitted to be listed in `throws()`.

Agree.  Typed throws should have a single thrown type.  Making it more 
convenient to throw and catch more than one error type with typed throws 
without having to manually create a wrapper is an orthogonal issue.  David 
Owens convinced me of this last year when we had a thread on the topic.

> 
> 
> On Fri, Feb 17, 2017 at 1:18 PM, Adrian Zubarev via swift-evolution 
> > wrote:
> That’s what I thought too. That’s also the case I showed a simple example how 
> we could solve the giant list with a typealias even if the pipe | operator 
> would be exclusive for anything that conforms to Error.
> 
> 
> 
> 
> -- 
> Adrian Zubarev
> Sent with Airmail
> 
> Am 17. Februar 2017 um 20:16:30, Anton Zhilin (antonyzhi...@gmail.com 
> ) schrieb:
> 
>> 2017-02-17 22:12 GMT+03:00 Adrian Zubarev via swift-evolution 
>> >:
>> 
>> 
>> Is the throwing type always a protocol? In your example it is, but is this 
>> going to be always the case?
>> 
>> 
>> I thought it was going to be any one subtype of 
>> Error, be it a struct, an enum, or a protocol existential, or 
>> Error itself.
>> 
> 
> 
> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org 
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> 
> 
> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Xiaodi Wu via swift-evolution
On Fri, Feb 17, 2017 at 1:22 PM, Adrian Zubarev via swift-evolution <
swift-evolution@swift.org> wrote:

> Sure thing, but that’s not what I was asking about. Kevin made a protocol
> that conforms to Error where all the his enums conformed to MyError
> protocol. That way we’re losing all enum cases and are not really a step
> further as before.
>

Sorry, can you elaborate? What do you mean by losing enum cases?

Am 17. Februar 2017 um 20:18:48, David Sweeris (daveswee...@mac.com)
> schrieb:
>
>
> On Feb 17, 2017, at 11:12 AM, Adrian Zubarev via swift-evolution <
> swift-evolution@swift.org> wrote:
>
> Is the throwing type always a protocol? In your example it is, but is this
> going to be always the case?
>
> I think right now, there’s a bit of compiler magic in that you can only
> throw something that conforms to `Error`.
>
> - Dave Sweeris
>
>
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Xiaodi Wu via swift-evolution
Let's not bring bikeshedding the commonly proposed and rejected union
spelling into this.
Typed throws would be a nice addition, assuming that the core team finds it
in scope for phase 2. It seems only logical that any type can be thrown
(i.e. conforms to Error) should be permitted to be listed in `throws()`.


On Fri, Feb 17, 2017 at 1:18 PM, Adrian Zubarev via swift-evolution <
swift-evolution@swift.org> wrote:

> That’s what I thought too. That’s also the case I showed a simple example
> how we could solve the giant list with a typealias even if the pipe |
> operator would be exclusive for anything that conforms to Error.
>
>
>
> --
> Adrian Zubarev
> Sent with Airmail
>
> Am 17. Februar 2017 um 20:16:30, Anton Zhilin (antonyzhi...@gmail.com)
> schrieb:
>
> 2017-02-17 22:12 GMT+03:00 Adrian Zubarev via swift-evolution <
> swift-evolution@swift.org>:
>
> Is the throwing type always a protocol? In your example it is, but is this
>> going to be always the case?
>>
> I thought it was going to be any one subtype of Error, be it a struct, an
> enum, or a protocol existential, or Error itself.
> ​
>
>
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
Sure thing, but that’s not what I was asking about. Kevin made a protocol that 
conforms to Error where all the his enums conformed to MyError protocol. That 
way we’re losing all enum cases and are not really a step further as before.



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:18:48, David Sweeris (daveswee...@mac.com) schrieb:


On Feb 17, 2017, at 11:12 AM, Adrian Zubarev via swift-evolution 
 wrote:

Is the throwing type always a protocol? In your example it is, but is this 
going to be always the case?

I think right now, there’s a bit of compiler magic in that you can only throw 
something that conforms to `Error`.

- Dave Sweeris
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread David Sweeris via swift-evolution

> On Feb 17, 2017, at 11:12 AM, Adrian Zubarev via swift-evolution 
>  wrote:
> 
> Is the throwing type always a protocol? In your example it is, but is this 
> going to be always the case?
> 
I think right now, there’s a bit of compiler magic in that you can only throw 
something that conforms to `Error`.

- Dave Sweeris___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
That’s what I thought too. That’s also the case I showed a simple example how 
we could solve the giant list with a typealias even if the pipe | operator 
would be exclusive for anything that conforms to Error.



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:16:30, Anton Zhilin (antonyzhi...@gmail.com) schrieb:

2017-02-17 22:12 GMT+03:00 Adrian Zubarev via swift-evolution 
:

Is the throwing type always a protocol? In your example it is, but is this 
going to be always the case?

I thought it was going to be any one subtype of  
Error, be it a struct, an enum, or a protocol existential, or  
Error itself.

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Anton Zhilin via swift-evolution
2017-02-17 22:12 GMT+03:00 Adrian Zubarev via swift-evolution <
swift-evolution@swift.org>:

Is the throwing type always a protocol? In your example it is, but is this
> going to be always the case?
>
I thought it was going to be any one subtype of Error, be it a struct, an
enum, or a protocol existential, or Error itself.
​
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
Is the throwing type always a protocol? In your example it is, but is this 
going to be always the case?



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 20:08:38, Kevin Nattinger (sw...@nattinger.net) schrieb:

protocol MyError: Error {}
enum MyFooError: MyError { … }
enum MyBarError: MyError { … }

func baz() throws(MyError)

On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution 
 wrote:

I suggest we need to find a way to shorten the list of the possible error types 
with a the help of typeallias

extension MyError1: Error { ... }
extension MyError2: Error { ... }
extension MyError3: Error { ... }

typealias MyErrors = MyError1 | MyError2 | MyError3   

func foo() throws(MyErrors) -> MyResult
func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> MyResult



-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 19:47:47, Anton Zhilin via swift-evolution 
(swift-evolution@swift.org) schrieb:

Now this is on-topic, I guess.
Last time we stopped at John McCall’s syntax:

extension MyError: Error { ... }

func foo() throws(MyError) -> MyResult
It’s conservative and prevents visual ambiguity with extra parentheses.

If we (somewhat) agree on this, then submitting a proposal will be trivial.

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread David Sweeris via swift-evolution

> On Feb 17, 2017, at 10:45 AM, Anton Zhilin via swift-evolution 
>  wrote:
> 
> Now this is on-topic, I guess.
> Last time we stopped at John McCall’s syntax:
> 
> extension MyError: Error { ... }
> 
> func foo() throws(MyError) -> MyResult
> It’s conservative and prevents visual ambiguity with extra parentheses.
> 
> If we (somewhat) agree on this, then submitting a proposal will be trivial.
> 
That mostly works for me. I’m a bit hesitant because I’m not sure how well that 
syntax would scale (if there are multiple types of errors that could be 
thrown). If the consensus is that such a scenario isn’t likely to be a problem 
in practice, then +1

- Dave Sweeris___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Kevin Nattinger via swift-evolution
protocol MyError: Error {}
enum MyFooError: MyError { … }
enum MyBarError: MyError { … }

func baz() throws(MyError)

> On Feb 17, 2017, at 11:03 AM, Adrian Zubarev via swift-evolution 
>  wrote:
> 
> I suggest we need to find a way to shorten the list of the possible error 
> types with a the help of typeallias
> 
> extension MyError1: Error { ... }
> extension MyError2: Error { ... }
> extension MyError3: Error { ... }
> 
> typealias MyErrors = MyError1 | MyError2 | MyError3  
> 
> func foo() throws(MyErrors) -> MyResult
> func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> MyResult
> 
> 
> 
> -- 
> Adrian Zubarev
> Sent with Airmail
> 
> Am 17. Februar 2017 um 19:47:47, Anton Zhilin via swift-evolution 
> (swift-evolution@swift.org ) schrieb:
> 
>> Now this is on-topic, I guess.
>> Last time we stopped at John McCall’s syntax:
>> 
>> extension MyError: Error { ... }
>> 
>> func foo() throws(MyError) -> MyResult
>> It’s conservative and prevents visual ambiguity with extra parentheses.
>> 
>> If we (somewhat) agree on this, then submitting a proposal will be trivial.
>> 
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org 
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> 
> 
> 
> ___
> swift-evolution mailing list
> swift-evolution@swift.org 
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> 

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Typed throws

2017-02-17 Thread Adrian Zubarev via swift-evolution
I suggest we need to find a way to shorten the list of the possible error types 
with a the help of typeallias

extension MyError1: Error { ... }
extension MyError2: Error { ... }
extension MyError3: Error { ... }

typealias MyErrors = MyError1 | MyError2 | MyError3  

func foo() throws(MyErrors) -> MyResult
func bar(_: () throws(T) -> Void) rethrows(MyErrors, T) -> MyResult


-- 
Adrian Zubarev
Sent with Airmail

Am 17. Februar 2017 um 19:47:47, Anton Zhilin via swift-evolution 
(swift-evolution@swift.org) schrieb:

Now this is on-topic, I guess.
Last time we stopped at John McCall’s syntax:

extension MyError: Error { ... }

func foo() throws(MyError) -> MyResult
It’s conservative and prevents visual ambiguity with extra parentheses.

If we (somewhat) agree on this, then submitting a proposal will be trivial.

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution