Oh look, a Result! Yes, this is a very useful type that should be part of the 
base language. Both guard / catch and Result would be great for Swift’s error 
handling.



Jon

> On Oct 8, 2017, at 2:01 AM, Tyler Cloutier via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> Also the Scala approach already works quite nicely in Swift 4 in case anyone 
> was curious:
> 
> enum Try<T> {
>     case success(T)
>     case failure(Error)
> 
>     init(_ f: @autoclosure () throws -> T) {
>         do {
>             self = .success(try f())
>         } catch {
>             self = .failure(error)
>         }
>     }
> }
> 
> 
> let handlerDisposableTry = Try(try startHandler(observer))
> 
> switch handlerDisposableTry {
> case .success(let x):
>     x?.dispose()
> case .failure(let error):
>     print(error)
> }
> 
> It’s just a touch awkward with the double try.
> 
> Tyler
> 
> 
>>> On Oct 7, 2017, at 10:42 PM, Tyler Cloutier <cloutierty...@aol.com> wrote:
>>> 
>>> try startHandler(observer) catch {
>>>     observer.sendFailed(error)
>>> }
>> 
>> 
>> Technically the above doesn’t make sense. Please disregard.
>> 
>>> On Oct 7, 2017, at 10:35 PM, Tyler Cloutier <cloutierty...@aol.com> wrote:
>>> 
>>> Has there been any progress on this? I came here to propose this but came 
>>> upon this thread first.
>>> 
>>> This proposal goes way beyond sugar. I find myself constantly in the 
>>> following situation:
>>> 
>>>         let observer = Observer(with: CircuitBreaker(holding: self))
>>>         do {
>>>             let handlerDisposable = try startHandler(observer)
>>>         } catch {
>>>             observer.sendFailed(error)
>>>         }
>>> 
>>>         cancelDisposable = ActionDisposable {
>>>             observer.sendInterrupted()
>>>             handlerDisposable?.dispose() // Error!!!! handlerDisposable is 
>>> not defined here
>>>         }
>>> 
>>> It’s not as simple as putting it all in the do block because then I’ll be 
>>> catching errors I might not want to catch! (In this case if the initializer 
>>> of ActionDisposable was capable of throwing.)
>>> 
>>> This is my frustration with every language that has this style of error 
>>> handling. FWIW, Scala is the only language that I have seen with this style 
>>> of error handling that solves this problem. The built-in type `Try` 
>>> combined with pattern matching fixes this issue of over extending the catch 
>>> area. Which is kinda nifty and also bridges the functional gap as well.
>>> 
>>> object Try {
>>>     /** Constructs a `Try` using the by-name parameter.  This
>>>     * method will ensure any non-fatal exception is caught and a
>>>     * `Failure` object is returned.
>>>     def apply[T](r: => T): Try[T] =
>>>       try Success(r) catch {
>>>         case NonFatal(e) => Failure(e)
>>>       }
>>>     }
>>> }
>>> 
>>> It also would make
>>> 
>>> try startHandler(observer) catch {
>>>     observer.sendFailed(error)
>>> }
>>> 
>>> an obvious extension which to me makes sense since it’s just treating the 
>>> one function call expression as it’s own implicit do block.
>>> 
>>> Tyler
>>> 
>>> 
>>> 
>>>> On Jul 11, 2017, at 10:31 AM, Christopher Kornher via swift-evolution 
>>>> <swift-evolution@swift.org> wrote:
>>>> 
>>>> 
>>>> 
>>>> Begin forwarded message:
>>>> 
>>>> From: Christopher Kornher <ckorn...@me.com>
>>>> Subject: Re: [swift-evolution] [Pitch] Guard/Catch
>>>> Date: July 10, 2017 at 5:10:15 PM MDT
>>>> To: Elviro Rocca <retired.hunter.dj...@gmail.com>
>>>> 
>>>> This messages was modified from the original accidentally sent out 
>>>> privately, earlier.
>>>> 
>>>> 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
>>>> ```
>>>> 
>>>> 
>>>> Most of the examples of this proposed feature don’t handle the exceptions 
>>>> other than to perform an early return. 
>>>> So, without handing exceptions,  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 
>>>> non-throwing function (or closure ) seems like an edge case to me. Perhaps 
>>>> I am just biased by my experience. I have not created or used many 
>>>> throwing initializers and certainly none in guard statements, and if they 
>>>> were in guard statements, the need to handle exceptions differently from 
>>>> any other guard failure seems ever more unlikely.
>>>> 
>>>> I don’t think that the small rightward drift of exception handling is 
>>>> onerous. It is hardly like the “pyramid of doom” that ```guard``` was 
>>>> created to fix.
>>>> 
>>>> ```
>>>> func f( y:Int? = nil )
>>>> {
>>>>     do {
>>>>         try bar()
>>>>         let x:X = try X()
>>>>         
>>>>         guard let y = y else {
>>>>             print( "No y")
>>>>             return
>>>>         }
>>>>         
>>>>         x.foo()
>>>>         print( "y=\(y)")
>>>>     } catch {
>>>>         // Handle some exceptions.
>>>>         return
>>>>     }
>>>> }
>>>> ```
>>>> 
>>>>> On Jul 10, 2017, at 1:45 AM, Elviro Rocca via swift-evolution 
>>>>> <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
>>>> 
>>>> 
>>>> 
>>>> 
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>> Il giorno 09 lug 2017, alle ore 01:16, Christopher Kornher via 
>>>>> swift-evolution <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.
>>>>> 
>>>>> 
>>>>> 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. : 
>>>>> 
>>>>>   ```
>>>>>   ```
>>>>>   
>>>>>   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:
>>>>> 
>>>>>   ```
>>>>>   ```
>>>>> 
>>>>>   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:
>>>>> 
>>>>>   ```
>>>>>   ```
>>>>> 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:
>>>>> 
>>>>>   ```
>>>>>   ```
>>>>> 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
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>> On Jul 8, 2017, at 4:16 PM, Benjamin Spratling via swift-evolution 
>>>>>> <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> 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, 
>>>>>>  guard try foo( ) catch { return }
>>>>>>  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
>>>>>>  }        
>>>>>>  guard {
>>>>>>          try fn1()
>>>>>>          try fn2()
>>>>>>          let x = fn3()
>>>>>>  } catch {
>>>>>>          // Must exit
>>>>>>  } else {
>>>>>>          // Must exit
>>>>>>  }        
>>>>>>  guard <your favorite character here>
>>>>>>          try fn1()
>>>>>>          try fn2()
>>>>>>          let x = fn3()
>>>>>>   <another favorite character here> catch {
>>>>>>          // Must exit
>>>>>>  } else {
>>>>>>          // Must exit
>>>>>>  }        
>>>>>> 
>>>>>> -Ben Spratling
>>>>>> 
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> 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
>>> 
>> 
> 
> _______________________________________________
> swift-evolution mailing list
> 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