I don't think that "guard let x? = ..." would be syntactically correct, AFAIK
for matching the pattern "let x? =", as for any other pattern, you need to use
"case", in fact the following is perfectly valid:
guard case let x? = doSomething() else {
// handle when nil
}
I think that "guard let x =" is in fact sugar for "guard case let x? =", so
your example should really be:
guard case let x? = try doSomething() catch {
// handle error
} else {
// handle when nil
}
This would basically mean "add an extra catch when pattern-matching with a
throwing function", same as:
switch try doSomething() {
case let x?:
// handle
case nil:
// handle
} catch {
// handle
}
Honestly I'm still not sold in conflating the two things. I don't think it
would be problematic to clearly separate cases when I'm binding the result of a
throwing function (which in fact is isomorphic to a Either<T,Error>) and
binding an Optional<T>.
Elviro
> Il giorno 10 lug 2017, alle ore 10:44, David Hart <[email protected]> ha
> scritto:
>
>>
>> On 10 Jul 2017, at 09:45, Elviro Rocca via swift-evolution
>> <[email protected] <mailto:[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] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution