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 >> <mailto: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 <mailto:swift-evolution@swift.org>> wrote: >>> >>> >>> >>> Begin forwarded message: >>> >>> From: Christopher Kornher <ckorn...@me.com <mailto: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 >>> <mailto: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 <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 >>> >>> >>> >>> >>> _______________________________________________ >>> 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> >>>> 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. >>>> >>>> >>>> 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 <mailto:swift-evolution@swift.org> >>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>> <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 <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, >>>>> 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 <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 >>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >> >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution