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.

Reply via email to