Thanks Tim for patiently explaining your perspective. Much appreciated. Please see my reply to Marcelo H showing using real life code that the outcome goes much further than replacing the err!= nil bit. On Monday, July 31, 2023 at 7:20:18 PM UTC-6 Tim Casey wrote:
> > >> You do not think this is a significant reduction in boilerplate? > > I understand people having a complaint about the verbosity of error > handling. But, this follows C code error handling. So to me, it is not > all *that* bad. > I think the measurable reduction in boilerplate code is ' ; err != nil' > for ' orelse '. And I do not believe this is worth a keyword. Some of > the context is being used to C. And another aspect is being used to perl > and its one liners. With perl, the debugger is not something which is > close to gdb/adb or the like. So the comparison is not about saving code. > > I would suggest, if I had a bold idea (and probably dumb), to have a > functional way to handle this. Something like: > > HandleErrorChain( todoList []func() (TYPE,error), errorHandler func () > TYPE, error ) (TYPE, error) > > Which means, I have a list of things I want to do and I want the error > handling to be all the same for all of them. Then the *chain* of things > would make sense with a single handler. This has the expense of being > kinda odd so at the very least it would need idiomatic polishing. I also > think I am too ignorant to have a real say in any of this. > > >> Why it won't work with one time retires? That is like one time retires > won't work in an if-else block > > I think there is a bit of talking past each other here. Or at least I > dont understand. I think orelse is too specific to be helpful. That is, > it is too precise to reduce a large swath of what might look like error > handling code, because error handling code can at times be specific. The > first dumb example I came up with is a simple one-time retry, which I have > put in code at times. It is usually ugly, but effective: > > v,err := dosomething() > if err != nil { > v,err = dosomething() > } > if err != nil { > handleError(err) > } > importantOperation(v) > > This with the token: > > if v,err := dosomething() orelse { > v,err = dosomething() orelse { > handleError() > } > } > importantOperation(v) > > The savings is the two 'err != nil' lines. > > If I take a step back, some of what I am reacting to is how much it is > discussed. It *feels*, at least to me as an ignorant person in this > context, as if people were not handling errors previously. Golang is > forcing this now and the resulting lines of code look like 'too much' > compared to previous lines of code. But I think this is a direct design > goal. If the keyword vs lines of code/tokenspace is worth it, so be it. > Who am I to say. If we are truely circling on a way to handle errors in a > chain, then the chain aspect is at least as important as any one line, and > up to this point has largely been ignored. > > In any event, i dont mean to be argumentative or even contrarian. Sorry > if it comes across that way. > > tim > > > On Mon, Jul 31, 2023 at 4:46 PM DrGo <salah....@gmail.com> wrote: > >> Thanks for the valuable feedback, >> The verbosity of error handling is the number one concern for Go >> developers in the most recent survey. So there is a need for doing >> something about it.. except that there are many possibly conflicting >> requirements outlined by the Go team. >> The problem with the abbreviated if err!= nil in your example: >> if x,err := something() ; err != nil { >> handleErrorAndReturn >> } >> is that x is local to the if scope and often that won't work forcing the >> more verbose: >> x,err := something() >> if err != nil { >> handleErrorAndReturn >> } >> If you have few of those as is the case in io programs; there is real >> impact on the readability of the code. >> the oresle approach avoids that problem (the x stays in the scope of the >> func). >> >> x,err := something() orelse { >> handleErrorAndReturn >> } >> You do not think this is a significant reduction in boilerplate? The >> only thing that will do a better job is a try-catch which is not acceptable >> to the Go team (fortunately). >> >> why the line-by-line would break with orelse? they are working fine with >> the one-liner you cited: >> if x,err := something() ; err != nil >> >> Why it won't work with one time retires? That is like one time retires >> won't work in an if-else block. >> >> Thanks again, >> >> On Monday, July 31, 2023 at 3:59:50 PM UTC-6 Tim Casey wrote: >> >>> >>> >>> I do not think this reduces boilerplate code. This compacts it, which >>> is different. >>> >>> I think any one-liner-return-on-err makes the language harder to debug. >>> It is very common breakpoints are set for exceptional cases, which tend to >>> be surprising. If the test and the return are on the same line then all of >>> the line-by-line tools will break down, at least a little bit. >>> >>> If you see: >>> x,err := something() ; err != nil { >>> handleErrorAndReturn >>> } >>> >>> At the very least, the template will not work for things like one time >>> retries, if error do something else, log the error and >>> DoSomethingAdditional(). This would be a sub scope and in golang i would >>> expect this to have '{}' as part of the scope shift. If this is >>> acceptable, then you are very likely to be on a separate line in any >>> event. So the original looks like: >>> >>> err := io.Copy(w, r) *orelse* { >>> DoSomethingElse() >>> } >>> >>> This means the only 'boilerplate' is 'orelse' <- '; err != nil', which >>> seems rather expensive for this error handling. >>> >>> As a slight change of subject, I find the whole discussion about >>> 'saving' boilerplate to be well over-stated, too much work and energy (at >>> least by me as an outside observer). Having something which fits within >>> the design of the language, making it a developer centric language, would >>> seem to fight with any one-line-template approach. >>> >>> tim >>> >>> >>> >>> >>> >>> The test and the handle all fit on one line. I dont think having a >>> single line return, like perl 'next if STATEMENT' style fits within golang >>> language goals. >>> >>> On Mon, Jul 31, 2023 at 8:18 AM DrGo <salah....@gmail.com> wrote: >>> >>>> Me too but I do not have high hopes >>>> >>>> >>>> On Monday, July 31, 2023 at 12:10:24 AM UTC-6 Mark wrote: >>>> >>>>> Given that this proposal is to reduce boilerplate, and assuming the >>>>> semantic issues could be solved, it seems to me that the 'return' is >>>>> redundant (i.e., could be implicit) and that 'orelse' could be done with >>>>> the existing 'else' keyword, i.e., >>>>> >>>>> ``` >>>>> result, err := someCall() else rest, err >>>>> ``` >>>>> Anyway, I really do hope the long-winded error syntax gets solved >>>>> somehow! >>>>> >>>>> On Monday, July 31, 2023 at 5:41:49 AM UTC+1 DrGo wrote: >>>>> >>>>>> func myFirstFunction() (string, err) { >>>>>> >>>>>> result, err := myFunction() orelse return rest, err >>>>>> >>>>>> } >>>>>> >>>>>> On Sunday, July 30, 2023 at 9:27:27 PM UTC-6 Marcello H wrote: >>>>>> >>>>>>> I think the current error handling is just fine. >>>>>>> For the extra typing, they invented keyboard snippets and such. >>>>>>> >>>>>>> But for this proposal, I would like to see how a return with >>>>>>> multiple values would look to get a better understanding. >>>>>>> ``` >>>>>>> // translate this in the proposed solution? >>>>>>> func myFirstFunction() (string, err) { >>>>>>> result, err := myFunction() >>>>>>> if err != nill { >>>>>>> return rest, err >>>>>>> } >>>>>>> } >>>>>>> ``` >>>>>>> >>>>>>> Op maandag 31 juli 2023 om 04:32:01 UTC+2 schreef DrGo: >>>>>>> >>>>>>>> Another possibility Jeremy is that the orelse block is executed if >>>>>>>> any of the returned error values is not nil. >>>>>>>> >>>>>>>> On Sunday, July 30, 2023 at 8:14:58 PM UTC-6 DrGo wrote: >>>>>>>> >>>>>>>>> Thanks... >>>>>>>>> yes indeed. Too many requirements but I think this solution comes >>>>>>>>> close to meeting them. If a rare function returns more than one error >>>>>>>>> value >>>>>>>>> (yet to see one in the wild) then the compiler should reject orelse >>>>>>>>> use and >>>>>>>>> the user can fallback on the (the if err!= nil) approach. >>>>>>>>> >>>>>>>>> On Sunday, July 30, 2023 at 6:02:57 PM UTC-6 Jeremy French wrote: >>>>>>>>> >>>>>>>>>> Also, errors are values, which means - although uncommon - a >>>>>>>>>> function could return two or more error values. Which would orelse >>>>>>>>>> evaluate? Even if you arbitrarily chose one, that would violate the >>>>>>>>>> explicit vs implicit code flow principle. >>>>>>>>>> >>>>>>>>>> My sympathies, OP. I too hate the "if err!= nil" boilerplate, >>>>>>>>>> and have suggested my own idea for fixing it, which was similarly >>>>>>>>>> dismantled for good reasons by those more knowledgeable than me. >>>>>>>>>> The truth >>>>>>>>>> is, this problem/issue has so many restrictions placed on it >>>>>>>>>> (currently >>>>>>>>>> idiomatic principles, backwards compatibility promise, explicit vs >>>>>>>>>> implicit, etc) that the set of possible solutions is VERY narrow, >>>>>>>>>> possibly >>>>>>>>>> infinitely so. >>>>>>>>>> >>>>>>>>>> On Sunday, July 30, 2023 at 3:51:49 PM UTC-4 Brian Candler wrote: >>>>>>>>>> >>>>>>>>>> err := io.Copy(w, r) *orelse* { >>>>>>>>>> w.Close() >>>>>>>>>> os.Remove(dst) >>>>>>>>>> return fmt.Errorf("copy %s %s: %v", src, dst, err) >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> My question still stands. Semantically, what value exactly does >>>>>>>>>> the "orelse" condition test is not equal to nil? >>>>>>>>>> >>>>>>>>>> - does it test the value from the preceding assignment? If so, is >>>>>>>>>> "orelse" only valid immediately following an assignment expression? >>>>>>>>>> The >>>>>>>>>> original posting didn't say this. And if it *is* linked to an >>>>>>>>>> assignment >>>>>>>>>> expression which assigns multiple values, does it only look at the >>>>>>>>>> last >>>>>>>>>> value? (Again, that was not specified) >>>>>>>>>> >>>>>>>>>> - does it always test a variable called "err"? The original >>>>>>>>>> posting said it was equivalent to "if err!=nil" but a later post >>>>>>>>>> contradicted this >>>>>>>>>> >>>>>>>>>> - does it test the value from the 'return' expression at the end >>>>>>>>>> of the block following orelse? Except in this case, it can't because >>>>>>>>>> it's >>>>>>>>>> buried inside fmt.Errorf >>>>>>>>>> >>>>>>>>>> On Sunday, 30 July 2023 at 17:55:34 UTC+1 DrGo wrote: >>>>>>>>>> >>>>>>>>>> Good point Harri, >>>>>>>>>> >>>>>>>>>> This is what the correct version will look like using this >>>>>>>>>> proposal >>>>>>>>>> >>>>>>>>>> func CopyFile(src, dst string) error { >>>>>>>>>> r, err := os.Open(src) *orelse* return fmt.Errorf("copy %s %s: %v >>>>>>>>>> ", src, dst, err) >>>>>>>>>> defer r.Close() >>>>>>>>>> >>>>>>>>>> w, err := os.Create(dst); *orelse* return fmt.Errorf("copy %s %s: >>>>>>>>>> %v", src, dst, err) >>>>>>>>>> err := io.Copy(w, r) *orelse* { >>>>>>>>>> w.Close() >>>>>>>>>> os.Remove(dst) >>>>>>>>>> return fmt.Errorf("copy %s %s: %v", src, dst, err) >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> err := w.Close() *orelse* { >>>>>>>>>> os.Remove(dst) >>>>>>>>>> return fmt.Errorf("copy %s %s: %v", src, dst, err) >>>>>>>>>> } >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> In a more complex func, the error formatting/handling code can be >>>>>>>>>> further deduplicated by extracting it into a closure. >>>>>>>>>> e.g., >>>>>>>>>> >>>>>>>>>> func CopyFile(src, dst string) error { >>>>>>>>>> copyErr:= func(err error) { >>>>>>>>>> return fmt.Errorf("copy %s %s: %v", src, dst, err) >>>>>>>>>> } >>>>>>>>>> r, err := os.Open(src) *orelse* return copyErr(err) >>>>>>>>>> defer r.Close() >>>>>>>>>> >>>>>>>>>> w, err := os.Create(dst); *orelse* return copyErr(err) >>>>>>>>>> err := io.Copy(w, r) *orelse* { >>>>>>>>>> w.Close() >>>>>>>>>> os.Remove(dst) >>>>>>>>>> return copyErr(err) >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> err := w.Close() *orelse* { >>>>>>>>>> os.Remove(dst) >>>>>>>>>> return copyErr(err) >>>>>>>>>> } >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> On Sunday, July 30, 2023 at 8:17:31 AM UTC-6 Harri L wrote: >>>>>>>>>> >>>>>>>>>> IMHO, you have used the irrelevant example (== 2nd code block) >>>>>>>>>> from Russ Cox's paper. The paper says: >>>>>>>>>> > This code is not nice, not clean, not elegant, *and still >>>>>>>>>> wrong:* like the previous version, it does not remove dst when >>>>>>>>>> io.Copy or w.Close fails. >>>>>>>>>> >>>>>>>>>> I want to compare your proposal with the third example from the >>>>>>>>>> paper, which does (proper) error annotation and cleanup. Thanks. >>>>>>>>>> On Sunday, July 30, 2023 at 8:57:15 AM UTC+3 DrGo wrote: >>>>>>>>>> >>>>>>>>>> I looked at the long list of proposals to improve error handling >>>>>>>>>> in go but I have not seen the one I am describing below. If I missed >>>>>>>>>> a >>>>>>>>>> similar , can you pls direct me to where I can find it. If not what >>>>>>>>>> do you >>>>>>>>>> think of this approach. >>>>>>>>>> >>>>>>>>>> This involves introducing a new keyword "orelse" that is a >>>>>>>>>> syntactic sugar for an "if err!=nil" block. >>>>>>>>>> >>>>>>>>>> The example code in Russ Cox's paper[1] will look something like >>>>>>>>>> this: >>>>>>>>>> >>>>>>>>>> func CopyFile(src, dst string) error { >>>>>>>>>> >>>>>>>>>> r, err := os.Open(src) orelse return err >>>>>>>>>> >>>>>>>>>> defer r.Close() >>>>>>>>>> >>>>>>>>>> w, err := os.Create(dst) orelse return err >>>>>>>>>> >>>>>>>>>> defer w.Close() >>>>>>>>>> >>>>>>>>>> err = io.Copy(w, r) orelse return err >>>>>>>>>> >>>>>>>>>> err = w.Close() orelse return err >>>>>>>>>> >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> It is an error to not return an error from an orelse block. >>>>>>>>>> >>>>>>>>>> In my eyes, this has the same explicitness and flexibility of the >>>>>>>>>> current style but is significantly less verbose. It permits ignoring >>>>>>>>>> the >>>>>>>>>> error, returning it as is or wrapping it. Because orelse is not used >>>>>>>>>> for >>>>>>>>>> any other purpose, it would be easy for reviewers and linters to >>>>>>>>>> spot lack >>>>>>>>>> of error handling. >>>>>>>>>> >>>>>>>>>> It also works well with named returns. e.g., >>>>>>>>>> >>>>>>>>>> func returnsObjorErro() (obj Obj, err error) { >>>>>>>>>> >>>>>>>>>> obj, err := createObj() orelse return //returns nil and err >>>>>>>>>> >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> otherwise orelse is like "else" so e.g., it can be followed by a >>>>>>>>>> block if additional cleanup or error formatting etc is needed before >>>>>>>>>> returning, eg >>>>>>>>>> w, err := os.Create(dst) orelse { >>>>>>>>>> .... >>>>>>>>>> return err >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> Similarity to "else" hopefully means that it is easy to learn. It >>>>>>>>>> is obviously backward compatible >>>>>>>>>> >>>>>>>>>> What do you think? >>>>>>>>>> >>>>>>>>>> [1] >>>>>>>>>> https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling-overview.md >>>>>>>>>> >>>>>>>>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "golang-nuts" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to golang-nuts...@googlegroups.com. >>>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/golang-nuts/b87365af-9a72-4f8d-ad0b-1ee69cc1ad35n%40googlegroups.com >>>> >>>> <https://groups.google.com/d/msgid/golang-nuts/b87365af-9a72-4f8d-ad0b-1ee69cc1ad35n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> -- >> You received this message because you are subscribed to the Google Groups >> "golang-nuts" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to golang-nuts...@googlegroups.com. >> > To view this discussion on the web visit >> https://groups.google.com/d/msgid/golang-nuts/9453a6fe-04c2-4c6d-88e9-4ec1cbd59361n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/golang-nuts/9453a6fe-04c2-4c6d-88e9-4ec1cbd59361n%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/14b505e9-9061-4a55-a309-69536dffcd85n%40googlegroups.com.