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 <[email protected]> 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 <[email protected]> wrote:
>>> On Thu, 22 Feb 2018 12:55:01 +0000 Jesper Louis Andersen
>>> <[email protected]> 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 [email protected].
>> 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 [email protected].
For more options, visit https://groups.google.com/d/optout.