Not sure what you mean here. A guard/catch would allow me to handle the error 
in the catch, something that (of course) cannot be done with "try?", so 
guard/else with "try?" is not a solution. And of course returning in the catch 
side of a do/catch is like returning in the else side of a if/else, but that's 
what "guard" is about, so I probably didn't understand what's your point here.


Elviro


> Il giorno 11 lug 2017, alle ore 01:10, Christopher Kornher <ckorn...@me.com> 
> ha scritto:
> 
> FYI this works today in Xcode 9.0 beta 2 playgrounds:
> 
> ```
> class X {
>     init() throws {}
> 
>     func foo() {
>         print( "Things succeeded" )
>     }
> }
> 
> func bar() throws -> X  { return try X() }
> 
> 
> func f()
> {
>     guard let x1:X = try? X(), let x2:X = try? bar() else {
>         print( "Things failed ")
>         return
>     }
> 
>     x1.foo()
>     x2.foo()
> }
> 
> f()        // works
> ```
> 
> So the only unhandled case is a non-returning throwing function or init:
>  
> ```
> class X {
>     init() throws {}
> 
>     func foo() {
>         print( "Things succeeded" )
>     }
> }
> 
> func bar() throws{ let _ =  try X() }
> 
> 
> func f()
> {
>     do {
>         try bar()
>     } catch {
>         return
>     }
> 
>     guard let x:X = try? X() else {
>         print( "Things failed ")
>         return
>     }
> 
>     x.foo()
> }
> 
> f()        // works
> ```
> 
> Having to call a throwing, Void method before performing the rest of a 
> function seems like an edge case to me 
> 
> 
>> On Jul 10, 2017, at 1:45 AM, Elviro Rocca via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 }
>> 
>> 
>> 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 <swift-evolution@swift.org 
>>> <mailto:swift-evolution@swift.org>> 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 
>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 <ckorn...@me.com 
>>>>> <mailto:ckorn...@me.com>> 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
>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>> 
>>>> -Ben Spratling
>>>> 
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto: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

Reply via email to