> On 10 Jul 2017, at 09:45, Elviro Rocca via swift-evolution 
> <[email protected]> wrote:
> 
> This is not a sugar proposal, in the same way as "guard" is not syntactic 
> sugar, because it requires exiting the scope on the else branch, adding 
> expressive power and safety to the call: also, the sugary part is pretty 
> important because it avoids nested parentheses and very clearly states that 
> if the guard condition is not fulfilled, the execution will not reach the 
> next lines of code. Guard is useful to push the programmer to at least 
> consider an early return instead of branching code paths, to achieve better 
> clarity, readability and lower complexity, and I suspect is one of the best 
> Swift features for many people.
> 
> Also, the case that the proposal aims to cover is not an edge case at all for 
> a lot of people, including me. Rethrowing an error is something that I almost 
> never do, and I consider the "umbrella" do/catch at the top of the call stack 
> an anti-pattern, but I understand that many people like it and I'm not 
> arguing against it. I am arguing in favor of having options and not pushing a 
> particular style onto programmers, and for my (and many people's) style, a 
> guard/catch with forced return is an excellent idea. In fact you seem to 
> agree on the necessity of some kind of forced-returnish catch but your 
> elaborations don't seem (to me) much better than the proposal itself.
> 
> Dave DeLong raised the point of weird behavior in the case of a function like:
> 
> 
> func doSomething() throws → Result? { … }
> 
> 
> In this case, what would the type of x be?
> 
> 
> guard let x = try doSomething() catch { /// handle and return }

I know we can’t do much about it now, but if optional binding had used the same 
syntax as it does in pattern matching, we wouldn’t be having this discussion:

guard let x = try doSomething() catch {
    // handle error
}

guard let x? = doSomething() else {
    // handle when nil
}

And mixing both would be a bit cleaner because the ? would make it explicit we 
are doing optional binding:

guard let x? = try doSomething() catch {
    // handle error
} else {
    // handle when nil
}

> Simple, it would be Optional<Result>. I don't find this confusing at all, and 
> if the idea that just by seeing "guard let" we should expect a non-Optional 
> is somehow diffused, I think it's better to eradicate it.
> 
> First of all, if I'm returning an optional from a throwing function, it's 
> probably the case that I want the Optional to be there in the returned value: 
> the only reason why I would consider doing that is if the semantics of 
> Optional are pretty meaningful in that case. For example, when parsing a JSON 
> in which I expect a String or null to be at a certain key:
> 
> 
> extension String: Error {}
> 
> func parseString(in dict: [String:Any], at key: String) throws -> String? {
>       guard let x = dict[key] else { throw "No value found at '\(key)' in 
> \(dict)" }
>       if let x = x as? String { return x }
>       if let x = x as? NSNull { return nil }
>       throw "Value at '\(key)' in \(dict) is not 'string' or 'null"
> }
> 
> 
> Thus, if I'm returning an Optional from a throwing function it means that I 
> want to clearly distinguish the two cases, so they shouldn't be collapsed in 
> a single call:
> 
> 
> guard let x = try doSomething() catch { /// handle and return }
> guard let x = x else { /// handle and return }
> 
> 
> Also, if a function returns something like "Int??", a guard-let (or if-let) 
> on the returned value of that function will still bind an "Int?", thus 
> unwrapping only "one level" of optional. If-let and guard-let, as of today, 
> just unwrap a single optional level, an do not guaranteed at all that the 
> bound value is not optional.
> 
> To me guard-let (like if-let) is basically sugar for monadic binding for 
> Optionals, with the additional expressivity granted by the forced return. I 
> would love to see the same monadic binding structure applied to throwing 
> functions.
> 
> 
> 
> Elviro
> 
> 
> 
>> Il giorno 09 lug 2017, alle ore 01:16, Christopher Kornher via 
>> swift-evolution <[email protected] 
>> <mailto:[email protected]>> ha scritto:
>> 
>> Thanks for you considerate reply. My concern over the proliferation of 
>> “sugar proposals” is a general one. This proposal has more merit and general 
>> utiliity than many others. I have never used a throwing function in a guard 
>> statement that was not itself in a throwing function, but I can see that it 
>> could possibly be common in some code. Wrapping a guard statement and all 
>> the code that uses variables set in the guard in a do/catch is sub-optimal.
>> 
>>> On Jul 8, 2017, at 4:16 PM, Benjamin Spratling via swift-evolution 
>>> <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>> 
>>> 
>>> I’ve read your email, but haven’t digested it fully.  One thing I agree 
>>> with is that most functions which call throwing functions don’t actually 
>>> use a do…catch block, but instead are merely marked “throws” and the error 
>>> is propagated back through the stack.  Once I seriously started coding 
>>> functions with errors, I realized I almost always wanted my errors to reach 
>>> my view-controller or my business logic so I could present separate UI if a 
>>> real error occurred, and often my error message depended on the details of 
>>> the error instance.
>>> 
>>> 
>>> 
>>> I disagree with your conclusion on this point.
>>> The “guard” syntax is specifically designed to achieve early return (and 
>>> placing code associated with early return at the point where it happens) 
>>> and cleanly installing the returned value into the surrounding scope.  So 
>>> far it has been used to achieve early return only with optionals, true.  
>>> But is that inherent to ‘guard’, or is it merely because that’s the only 
>>> way it has been used?  The guard does set variables that are needed in the 
>>> body of the function, and that’s exactly why using guard with values 
>>> returned from throwing functions makes so much sense, because it does 
>>> exactly the same thing in a general sense.  The “do”…”catch” structure is 
>>> intentionally designed differently, to place the “happy path” in one place 
>>> and place the returns in another place.  I think with guard/else, we’re 
>>> seeing developers who can handle less cognitive loading find it easier to 
>>> reason about early return than grouping failures after the happy path.  
>>> This proposal hopes to introduce that better language architecture to the 
>>> catching of errors.
>>>> On Jul 8, 2017, at 4:08 PM, Christopher Kornher <[email protected] 
>>>> <mailto:[email protected]>> wrote:
>>>> 
>>>> I am opposed to this proposal because it muddies up the language to 
>>>> support what is essentially an edge case. The standard way to exit a 
>>>> function early because an exception is thrown is to make the function 
>>>> itself throw, if it is part of a larger operation. The addition of a few 
>>>> lines of try/catch code is not a great burden and makes the termination of 
>>>> an an exception very clear.
>>>> `guard` statements are generally used to set variables that are needed in 
>>>> the body of a function. Using them to save a few lines of exception 
>>>> handing code is a very different use. There is no need to mix two 
>>>> relatively clean syntaxes for a few edge cases and increase cognitive load 
>>>> one more time, 
>> 
>> All catches don’t have to exit the outer scope, so using guard only handles 
>> a subset 
>> 
>> It think that creating the terse try/catch for simple cases has multiple 
>> advantages:
>> 
>>      1) I think that it addresses your desire for a simple way to use 
>> throwing functions easily in guard statements.
>> 
>>      2) It avoids having to change the guard syntax to accomplish this
>> 
>>      3) It is useful for handling simple one line try/catch constructs in 
>> less space in a way that should not seem too foreign to Swift developers.
>>      
>>      4) It simplifies code that currently uses nested do/try/catch 
>> constructs. Even though this is rare, it introduces significant “rightward 
>> drift”.
>> 
>>      5) It can used to return early from void throwing functions easily. 
>> e.g. : 
>> 
>>      ```
>>>     guard try foo( ) catch { return }
>>      ```
>>      
>>      Multiple void throwing functions would probably be better handled by a 
>> do/catch block, but there is no danger of needing values from these 
>> functions because there are none:
>> 
>>      ```
>>>     do {
>>>             try fn1()
>>>             try fn2()
>>>     } catch {
>>>             // keep going, return or call a non-returning function, since 
>>> throw is already handled by declaring a throwing enclosing function.
>>>             // No varibles are needed by the outer block because none are 
>>> set
>>>             // So it is not clearly a guard-like statement
>>>     }        
>>      ```
>> 
>>      I did not think of this before, but perhaps we could allow `do` to be 
>> replaced with `guard`, thereby allowing values to escape to the outer scope, 
>> while still ensuring an early exit:
>> 
>>      ```
>>>     guard {
>>>             try fn1()
>>>             try fn2()
>>>             let x = fn3()
>>>     } catch {
>>>             // Must exit
>>>     } else {
>>>             // Must exit
>>>     }        
>>      ```
>> I am not sure that “leaky” braces are a good idea, so perhaps some other 
>> character could be used to indicate a non-scope or whatever you want to call 
>> it:
>> 
>>      ```
>>>     guard <your favorite character here>
>>>             try fn1()
>>>             try fn2()
>>>             let x = fn3()
>>>      <another favorite character here> catch {
>>>             // Must exit
>>>     } else {
>>>             // Must exit
>>>     }        
>>      ```
>> This would make the language even harder to read, so just using braces is 
>> probably a better idea.
>> 
>> This would change the guard syntax slightly, but is a straightforward 
>> extrapolation of do/catch and guard, I think. Of course, this could replace 
>> the existing guard syntax entirely and its use of semicolons, if we want to 
>> go that far…
>> 
>> Allowing this syntax only if one of the expressions throws is possibly a 
>> good backward-compatible solution that would avoid redundant guard syntaxes.
>> 
>> Anyway there are lot of possibilities here. We are not forced to extend the 
>> guard statement as it exists today. The current guard statement syntax was 
>> quite controversial when it was introduced and extending it may not be the 
>> best option to do what you want.
>> 
>> - Chris
>> 
>> 
>> 
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>>> -Ben Spratling
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> [email protected] <mailto:[email protected]>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to