1. 7
  1. This story is almost 90 days old and will stop accepting comments in 14 minutes.

     

    1. 14

      AFAIU, the point of the error handling style in Go is to make it easy to add context to errors (and also add “special handling” code in case errors are encountered). It would be nice if this proposal focused on how it works in such situations - which is what IMO is the default approach to consider in idiomatic Go code, instead of “naked” return err. Which, yes, has its places, but by default, I expect to need to be explained when reviewing Go code. In this proposal, it’s not clear to me at first glance if fmt.Errorf is supported nicely. Which I’d expect per the adage of “make the idiomatic thing easy and simple, make the non-idiomatic thing possible but cumbersome”.

      1. 23

        Yup.

        Concretely,

        For example, the following code demonstrates traditional Go error handling:

        _, err = fd.Write(p0[a:b])
        if err != nil {
            return err
        }
        

        No, traditional Go error handling would annotate the error,

        _, err = fd.Write(p0[a:b])
        if err != nil {
            return fmt.Errorf("write p0: %w", err)
        }
        
        1. 1

          It would be possible to write a function that returns a handle function.

          _, err = fd.Write(p0[a:b]) ? handler("write p0: %w")
          
          1. 5

            Yep! But sometimes the annotation needs to include stuff other than just the annotated error, e.g.

            _, err = fd.Write(p0[a:b])
            if err != nil {
                return fmt.Errorf("write p0 (%d:%d): %w", a, b, err)
            }
            

            Meaning the proposal would need to look something like

            _, err = fd.Write(p0[a:b]) ? handler("write p0 (%d:%d): %w", a, b, err)
            

            Personally, I don’t really see the value there.

            1. 1
              package fmt
              
              // Check returns a function that can be used with the ? operator.
              // If format ends with an extra %w verb, the error is wrapped as the final argument.
              func Checkf(format string, a ...any) check
              

              Personally, I don’t really see the value there.

              I agree

    2. 9

      Honestly at this point I’d rather just keep Go’s error handling as it is. It’s verbose but at least it’s explicit, trivial, and can do whatever you need (wrap, log, transform, return anything)… when you actually work with Go, the verbosity just becomes a non-issue, and maybe even a huge pro.

      This idea also has many issues on its own: is the handle function signature infinitely dynamic for functions that return 1, 2, n values? Does the ? operator only work with functions returning an error as their last result? Why does the handler even need to be aware of return values besides the error since it can’t access the function context (to return maybe partial results additionally to the error) ? I don’t think it’s a well thought out proposal.

    3. 3

      I heard there was already a proposal a few years ago, by the Go core team itself, to improve error handling ergonomics in a similar way, but it was rejected by the community?

      the proposer believes that due to its design based on higher-order functions, people may quickly adapt to this new error handling approach

      Based on the confusion and irritation over the new Iterator API, I would assume Go developers would be less welcoming to a new feature because it used HOF.

    4. 2

      The summary at the end feels very LLM generated.

    5. 1

      When the ret function is not called, the entire f() ? handle expression returns the value passed to the handle function

      If this is the case, then why does the handle function need a return value?

      edit:

      Actually why does the handle function need any of the other values? It could be made to only deal with errors:

      func handle(ret func(error), err error) {
          if err != nil {
              ret(err)
          }
      }
      
    6. 1

      ret is, effectively, a captured continuation, and could be saved in a data structure outliving the intended extent. I imagine you’d have to either change the entire runtime to account for this possibility, or introduce some sort of cancellation that panics when ret is called again. A sort of “cancelable sync.Once”

    7. 1

      I quite like this. It should be proposed on the issue tracker, unless I missed the link.