> 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 <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 >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution