[swift-evolution] Idea: Extend "guard" to try-statements, with a catch block
Erica Sadun
erica at ericasadun.com
Tue Mar 8 18:11:14 CST 2016
Regardless of syntax, I'm still missing the point on this one.
If there's error-handling and recovery work to be done, isn't this better done without a guard statement, either using `try` to allow the calling chain to respond or using a `do`-`catch` construct. `guard` guarantees scope exit on failure, but so does `try` outside of a`do`-`catch`.
* If you care about just logging errors, you can use a printing form of `try?`. I brought this up on another thread.
* If you care about mitigating a failure, then you should give the `catch` clause the due space and ability to handle recovery that it deserves.
* If you don't care about printing the error, why not use normal `guard` and `try?`.
What is the point of only catching specific errors, or specific types of errors, or etc that deserves a language change here, where it's merged into `guard`?
-- E, dense
> On Mar 8, 2016, at 4:56 PM, Jordan Rose via swift-evolution <swift-evolution at swift.org> wrote:
>
> Another possible syntax:
>
> guard try let x = foo()
> catch let error as FooError {
> return nil
> }
>
> Downsides:
> - You can't limit the 'try' to only part of the expression.
> - Actually catching a specific error makes for a very long line, to the point where I automatically wrapped it anyway. Maybe we should just allow one catch.
> - It still feels like it should be testing for an optional (because of the binding syntax).
>
> Jordan
>
>
>> On Mar 8, 2016, at 11:37, Jacob Bandes-Storch via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>
>> Thanks for the feedback, Chris.
>>
>> To clarify, are you suggesting that plain variable bindings would support this too (such as "let x = try foo() catch { ... }")? I like this idea. I can also foresee the desire for something like this:
>>
>> let x = try foo() catch {
>> print(error)
>> x = bar() // by some syntax, provide a fallback value for x, since foo() threw
>> // control flow *can* escape, since x has a value
>> }
>>
>> Of course, you can achieve this today with "let x; do { x = try foo() } catch { x = bar() }", or with "let x = try? foo() ?? bar()". I just wonder if it's worth considering the possibility that this new feature would allow control flow to escape in some cases. (After all, control flow can exit the "catch" block we have today.) But it's also nice to be able to glance at the code and see that, unambiguously, control flow can't escape the block.
>>
>> The reason I originally suggested "guard" is readability: Seeing the word "guard", a reader knows that control flow can't escape the else/catch block. But I understand why guard is still necessary for working with optionals/patterns, and I suppose seeing "try" introducing the expression may be enough.
>>
>> Do you have thoughts on whether else/catch blocks should be re-orderable?
>>
>> Another question: should it work with expressions that don't bind variables? Simply "try foo() catch { ... }" ? (At one point I had considered "do try ... catch ...", as a braceless analogue of "do { try ... } catch ...".)
>>
>> Jacob
>>
>> On Mon, Feb 29, 2016 at 10:34 PM, Chris Lattner <clattner at apple.com <mailto:clattner at apple.com>> wrote:
>> On Feb 29, 2016, at 12:09 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> I propose extending guard-statements to handle errors without using the optional "try?" and without a do-block, by allowing the expression to throw, and offering "catch" instead of "else":
>>>
>>> // func foo() throws -> T ...
>>> guard let x = try foo catch {
>>> print("the error was: \(error)") // the "error" parameter is available here
>>> // the compiler does not allow control flow to escape this block
>>> }
>>> // control flow cannot reach here if foo() threw an error
>>
>> I don’t think that this syntax makes sense, because you’re changing "guard let x = ” to not test an optional. The syntax you’re searching for seems more consistent as a modifier on var/let itself:
>>
>> // func foo() throws -> T ...
>> let x = try foo() catch {
>> print("the error was: \(error)") // the "error" parameter is available here
>> // the compiler does not allow control flow to escape this block
>> }
>>
>> The guard form of this would still make sense, but the existing “guard let” and “guard case” matching should work as it does. For example, something like this should be allowed:
>>
>> // func bar() throws -> T?
>> guard let x = try bar() else {
>> // this runs if ‘bar’ returns nil.
>> // the compiler does not allow control flow to escape this block
>> } catch {
>> print("the error was: \(error)") // the "error" parameter is available here
>> // the compiler does not allow control flow to escape this block
>> }
>>
>> More generally, the “guard” form would be workable on anything that takes a stmt-condition. This brings “if" and “while” into the mix.
>>
>> -Chris
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160308/77edf996/attachment.html>
More information about the swift-evolution
mailing list