r := os.Open(filename) if isError(r) { return r.(error) } f := r.(*os.File) // or better: var f *os.File; f = r
Error checking being a common pattern, isError() can be added as a builtin or trivially in errors pkg. Likely the enclosing function also returns an error so the return in the second line above can be just return r but with the current product type approach you’d have return nil, err. You are only looking at code after returning. Code within a function benefits more (gets simplified). func f(s string) (int|error) { // instead of (int,error) ... return err // instead of return 0,err ... return 1 // instead of return 1,nil // and return n, err case disappears Having to return an extra thing that gets discarded right away is annoying. Early on this was a common mistake for me. We are used to it now but the current idiom came about because of a lack of sum types. People can use object{} as a return type today but that more or less defeats type checking which may be why returning an extra thing was perhaps seen as acceptable. So yes, I do think sum types benefit here. May be not enough for people to want a change but net benefit nonetheless. The bigger benefit is their use where people currently end up using object{} or product type. With sum types you get better documentation and checking. If you think of sum types as just restricted object{} types, they are simple to explain and implement. Simpler than what you guys were discussing on issue 19412 thread. I will add a comment there. On Feb 24, 2018, at 12:03 PM, roger peppe <rogpe...@gmail.com> wrote: >> I once counted there were about 8000 uses of <type>,error as >> return types for functions in $GOROOT/src which could benefit >> from sum types. > > I'm not entirely convinced that sum types are a net win as a > substitute for (T, error) return values. > > Where currently we have: > > f, err := os.Open(filename) > if err != nil { > return err > } > // use f > > we'd have to do something like: > > fOrError := os.Open(filename) > if err, ok := fOrError.(error); ok { > // ... but what happens if we want to return a distinct > // value that implements error? > return err > } > f := fOrError.(*os.File) > // use f > > or perhaps: > > var f *os.File > switch fOrError := os.Open(filename).(type) { > case *os.File: > f = fOrError > case error: > return fOrError > } > // use f > > or even, defensively (perhaps os.Open's type might change to admit > more possibilities: > > var f *os.File > switch fOrError := os.Open(filename).(type) { > case *os.File: > f = fOrError > case error: > return fOrError > default: > panic("unexpected type %T returned from os.Open", fOrError) > // or return error > } > // use f > > I don't know about you, but I wouldn't consider any of those a > particular improvement on the current idiom. They're all more verbose > and not actually that much more type-safe. > > That doesn't mean that I don't think sum types are a decent idea, but > just that I'm not sure that they're as clear a win for this (a > commonly cited use case) as one might think. > > cheers, > rog. > > >> On 24 February 2018 at 01:23, Bakul Shah <ba...@bitblocks.com> wrote: >>> On Thu, 22 Feb 2018 12:55:01 +0000 Jesper Louis Andersen >>> <jesper.louis.ander...@gmail.com> wrote: >>> >>> For sums, in Go, I have to return a pair, >>> >>> x, err := someOperation(..) >>> >>> but this is slightly inconsistent, insofar I can return "nil, nil", which >>> might not be a valid value, or I can return "3, Error(..)" but in that case >>> the 3 is not to be used. Only one "side" is valid at a given point in time. >>> If you have sum-types in the usual sense, you can define something along >>> the lines of (OCaml follows): >>> >>> type ('a, 'b) result = Ok of 'a | Error of 'b >> >> I once worked out some details of adding sum types to Go and I >> think it is quite doable and easy to implement. For example, >> >> func f(i int) float64|error { >> if i == 0 { return errors.New("not zero") } >> return 1./float64(i) >> } >> >> As in OCaml "|" is used for sum types and it binds less >> tightly than existing type "expressions". >> >>> And then you can discriminate on this value via pattern matching >>> >>> match res with >>> | Ok value -> ... >>> | Error err -> ... >> >> Not quite the same but something similar is doable with >> type switch. >> >> res := f(j) >> switch res.(type) { >> case error: ... >> case string: ... >> } >> >> This use is identical with f returning interface{} (even the f >> body remains exactly the same). This makes sense since >> interface{} is in a sense the sum of all other types. >> >> But by allowing sum type, we can do better checking within f >> (e.g. return "string" will fail to compile). And by using a >> sum type instead of a product type to return a value or error >> also makes the code clearer. >> >> I once counted there were about 8000 uses of <type>,error as >> return types for functions in $GOROOT/src which could benefit >> from sum types. Now there seem to be about 4253 instances >> (found using a crude regexp). >> >> I think I worked out the semantics of T1|T2 where T1 and T2 >> are both interface types themselves. It all seem to fit >> together, at least on paper! I need to find my notes.... >> >>> The other matching construction is a switch-statement, but such a statement >>> doesn't allow for matching deeply into an AST structure, which a >>> traditional pattern matcher does. >> >> Deeper matching also binds names to matched parts. e.g. >> >> sumsqr [] = 0 >> sumsqr (x:xs) = x*x + sumsqr xs >> >> This sort of binding may be difficult to shoehorn into Go. >> There may be no real benefit of binding head, tail of a slide >> but consider an AST. If you are already cracking it open for >> matching it with a pattern, you may as well bind variables to >> interesting parts. >> >> match stmt { >> case If1stmt(test:, thenstmt:): ... >> case If2stmt(test:, thenstmt:, elsestmt:): ... >> ... >> } >> >> Hard to come up with an intuitive syntax here. Also probably >> impossible to add func level patterns. >> >>> Coincidentally, given sum-types, you can write a regexp matching engine in >>> very few lines of code. See Bob Harper's "Programming in Standard ML" [2] >>> for example; it is the introductory example to get a feel for the language. >>> The solution uses sum types to define the AST for regular expressions, and >>> then uses pattern matching to build a matcher on regular expressions. I >>> can't remember how far Bob takes the exposition however. >> >> This should be doable given my sum-type proposal sketch above?! >> >>> [0] They are really the same in the right setting. In a boolean algebra, >>> for instance, + is OR and * is AND. If you look at them from the Category >>> Theory branch of mathematics they are related: they are duals of each other >>> which means that if you "invert" one you get the other and vice versa. >>> >>> [1] Obviously, Go, being a descendant of Algol has two syntactic classes: >>> statements and expressions, whereas OCaml is descendant from either Lisp or >>> the Lambda Calculus depending on view. Those languages only have >>> expressions as a syntactic class. >> >> sum-types (which are easy to implement) and pattern matching >> (may be not so easy) can be added to Go even if it has >> non-expression syntatic classes. >> >>> [2] http://www.cs.cmu.edu/~rwh/isml/book.pdf >>> >>> [3] >>> https://www.microsoft.com/en-us/research/wp-content/uploads/1987/01/slpj-book-1987-small.pdf >> >> SLPJ's book was quite an eye opener for me back in the '80s! >> >> -- >> 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. >> For more options, visit https://groups.google.com/d/optout. -- 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. For more options, visit https://groups.google.com/d/optout.