> 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