FAQ
A really hefty percentage of my code consists of:

if err != nil {
     return err
}

I'm proposing a shortcut that maintains equivalent semantics, but less text:

The @ symbol prepended before a named variable in the return of list of
current method (ErrorPassAlonger's) return list:

func ErrorPassAlonger() (value string, err Error) {
     @err = GenerateError()

     ...
}

Which expands to the following:

func ErrorPassAlonger() (value string, err Error) {
     err = GenerateError()
     if err != nil {
         return
     }
}

This could easily work with multiple assignments on the left hand side:

a, b, @err := DoSomething()

And, because it is defined to use a naked return, it works for multiple
return values from ErrorPassAlonger.

An alternative that makes it more clear that this is about error handling
is to define the @ expansion as returning all nil values, other than the
designated value.

We could also say that a naked @ in an assignment list is the equivalent of
@err:
     @ = GenerateError()


PS: I'm not sure this is a super-awesome-must-implement proposal, but I'd
really like SOME way of reducing the amount of dumb error handling boiler
plate in my code.

--
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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Search Discussions

  • Jan Mercl at Sep 7, 2014 at 7:09 pm

    On Sun, Sep 7, 2014 at 8:35 PM, Don Garrett wrote:
    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less text:
    Is less text desirable in this case? If so, why? Is it supposed to
    improve something? If so, what?

    -j

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Don Garrett at Sep 7, 2014 at 7:48 pm
    I recently wrote a method that was 11 lines long and very straight forward,
    until I went back to add the error handling. It was then 30 lines long. If
    the majority of the method is boilerplate, that sets off red flags for me.

    I find that given equal readability (VERY important constraint), the
    shorter code is usually the best.

    Mentally, it takes less effort to follow all the details of something if
    you can see it all at one time. It's also easier to follow, if it has fewer
    distractions built into it.

    Each little distraction can be filtered out and ignored, but that filtering
    adds just a little more cognitive load, and that takes away from your main
    task. My proposal above may or may not be good in this regard, because it
    could be hiding import flow a little too much.

    In addition, that very filtering can make it easy to skip over variations
    from the norm without realizing it. For example, if an error handler uses
    continue instead of return.

    Finally, the boilerplate can make simple changes more expensive than they
    need to be. If I have a method with a single return in the normal case, but
    10 error returns, I may have to edit 12 locations to change the return list
    instead of 2. (I tend to explicitly return nil values for non-errors in
    error handlers).

    I really like a LOT of stuff about Go, but amount of boilerplate around
    error handling drives me up a wall, especially since the rest of the
    language has so wonderfully balanced sparseness with precision.

    On Sun, Sep 7, 2014 at 12:08 PM, Jan Mercl wrote:
    On Sun, Sep 7, 2014 at 8:35 PM, Don Garrett wrote:
    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less
    text:

    Is less text desirable in this case? If so, why? Is it supposed to
    improve something? If so, what?

    -j


    --
    Don

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Dmitri Shuralyov at Sep 8, 2014 at 4:31 am
    When writing code, there are many different contexts.

    One common context/pattern is writing a "try your best to get some value,
    or return err if cannot" function.

    Within such function, you know well in advance you want to do essentially
    this for each step:

    v1, v2, v3, err := foo1(v4, v5)
    if err {
    return nil, nil, Foo{}, err
    }

    If anything returns non-nil err, just bail out immediately and forward the
    err.

    Imagine you need to do 5 such steps to explore an idea in your head. You
    want to run this func once and depending on the result, do something else
    next.

    It's the difference of having to write:

    os.Chdir("..")
    b := io.ReadFile("foo.txt")
    ...
    return Foo, result, "success", n, nil

    vs.

    err := os.Chdir("..")
    if err != nil {
    return Foo{}, nil, "", 0, err
    }

    b, err := io.ReadFile("foo.txt")
    if err != nil {
    return Foo{}, nil, "", 0, err
    }

    ...

    return Foo, result, "success", n, nil

    Once that code is written it's fine. The extra verbosity makes it very easy
    to change error handling behavior, and understand what happens exactly. But
    typing it out the first time is a lot of work.

    This is just one of those things, where when you have an idea and you want
    to quickly test it out, having the default be "ignore all errors" doesn't
    feel optimal.

    A counter example, if you want to swap two var values, being able to write:

    v1, v2 := v2, v1

    Feels very nice.

    Also, being able to write a new .go file where you add a log.Panicln() line
    for the first time, press save and have goimports automatically add "log"
    import for you (and later remove it when you remove the log.Panicln() line)
    is nice.

    Just trying to share some motivation for why there might be benefits to
    comping up with an elegant idea here.

    On Sunday, September 7, 2014 5:02:50 PM UTC-7, Lars Seipel wrote:
    On Sun, Sep 07, 2014 at 12:48:47PM -0700, Don Garrett wrote:
    Each little distraction can be filtered out and ignored, but that filtering
    adds just a little more cognitive load, and that takes away from your main
    task.
    Maybe that's where your problem is rooted. Don't think of error handling
    as an distraction. It's an essential part of your program. It *is* your
    main task. Just like handling all the other things your program deals
    with.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Don Garrett at Sep 8, 2014 at 5:59 am
    For some reason, I started hitting Reply instead of Reply All today, so
    resending....

    I see generating errors, and handling them as very important, but I really
    don't see passing them up the call chain blindly (10 times in a row) as
    anything but boiler plate. Admittedly, I've been working in languages with
    strong exception support for years, and I'm used to thinking that way.
    On Sun, Sep 7, 2014 at 9:30 PM, Dmitri Shuralyov wrote:

    When writing code, there are many different contexts.

    One common context/pattern is writing a "try your best to get some value,
    or return err if cannot" function.

    Within such function, you know well in advance you want to do essentially
    this for each step:

    v1, v2, v3, err := foo1(v4, v5)
    if err {
    return nil, nil, Foo{}, err
    }

    If anything returns non-nil err, just bail out immediately and forward the
    err.

    Imagine you need to do 5 such steps to explore an idea in your head. You
    want to run this func once and depending on the result, do something else
    next.

    It's the difference of having to write:

    os.Chdir("..")
    b := io.ReadFile("foo.txt")
    ...
    return Foo, result, "success", n, nil

    vs.

    err := os.Chdir("..")
    if err != nil {
    return Foo{}, nil, "", 0, err
    }

    b, err := io.ReadFile("foo.txt")
    if err != nil {
    return Foo{}, nil, "", 0, err
    }

    ...

    return Foo, result, "success", n, nil

    Once that code is written it's fine. The extra verbosity makes it very
    easy to change error handling behavior, and understand what happens
    exactly. But typing it out the first time is a lot of work.

    This is just one of those things, where when you have an idea and you want
    to quickly test it out, having the default be "ignore all errors" doesn't
    feel optimal.

    A counter example, if you want to swap two var values, being able to write:

    v1, v2 := v2, v1

    Feels very nice.

    Also, being able to write a new .go file where you add a log.Panicln()
    line for the first time, press save and have goimports automatically add
    "log" import for you (and later remove it when you remove the log.Panicln()
    line) is nice.

    Just trying to share some motivation for why there might be benefits to
    comping up with an elegant idea here.

    On Sunday, September 7, 2014 5:02:50 PM UTC-7, Lars Seipel wrote:
    On Sun, Sep 07, 2014 at 12:48:47PM -0700, Don Garrett wrote:
    Each little distraction can be filtered out and ignored, but that filtering
    adds just a little more cognitive load, and that takes away from your main
    task.
    Maybe that's where your problem is rooted. Don't think of error handling
    as an distraction. It's an essential part of your program. It *is* your
    main task. Just like handling all the other things your program deals
    with.

    --
    Don

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Andrew Gerrand at Sep 8, 2014 at 6:11 am

    On 8 September 2014 15:59, Don Garrett wrote:

    I see generating errors, and handling them as very important, but I really
    don't see passing them up the call chain blindly (10 times in a row) as
    anything but boiler plate. Admittedly, I've been working in languages with
    strong exception support for years, and I'm used to thinking that way.

    It's boilerplate until you need to change the way those errors are handled.
    Then it's easy to customise the behaviour however you wish. Earlier you
    made an argument in favour of readability. I argue that code that states
    exactly what is happening is great for readability.

    I can't speak to your specific example, but blindly returning errors up the
    call stack is probably a hint that you're not handling errors very well.

    It is a good practice to adorn errors with context before you return them.
    This is especially important at package boundaries.

    Here's an example:
    https://gist.github.com/peterbourgon/10527082#file-foo-go-L8

    On 8 September 2014 16:08, Don Garrett wrote:

    I started looking through my current project, and found something
    interesting. Intuitively, I expected to find a lot examples in which the
    boiler plate error handling lines exceeded the rest of the lines. I did
    find some, but not nearly as many as I was expecting, and the majority of
    the ones I found were short.

    It may well be that I'm writing a lot of methods with excessive boiler
    plate, them reducing it as I get them closer to finished.
    This is a common experience. Go's error handling seems verbose at first,
    but once you start using it in real programs (and get better at writing Go
    code), it becomes less repetitive. This is because, as has been stated
    elsewhere in this thread, Go errors are just values, and you program with
    them just as you do other values. As you get better at writing Go code, so
    do you get better at handling Go errors.

    Andrew

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Omarshariffdontlikeit at Sep 8, 2014 at 6:06 am

    "I recently wrote a method that was 11 lines long and very straight
    forward, until I went back to add the error handling. It was then 30 lines
    long. If the majority of the method is boilerplate, that sets off red flags
    for me."
    Error handling is not boilerplate, it IS the program.

    Mentally, it takes less effort to follow all the details of something if
    you can see it all at one time. It's also easier to follow, if it has fewer
    distractions built into it.
    The explicit error handling ARE the details. Error handling is NOT
    distraction.

    Finally, the boilerplate can make simple changes more expensive than they
    need to be. If I have a method with a single return in the normal case, but
    10 error returns, I may have to edit 12 locations to change the return list
    instead of 2. (I tend to explicitly return nil values for non-errors in
    error handlers).
    The explicit error handling in this example has helped you to ensure you
    haven't missed a case where you would have to modify the return. In your
    example, it actually HELPED to ensure your code was correct and that you
    understood each and every return was impacted by this "simple" change. One
    could argue that modify the signature for a method/function is in no way a
    simple change.

    I really like a LOT of stuff about Go, but amount of boilerplate around
    error handling drives me up a wall, especially since the rest of the
    language has so wonderfully balanced sparseness with precision.
    Looks to me that the "boiler plate" has in fact ensured your code is
    correct and error free.

    I really don't see the problem here, I only see benefits.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Justin Israel at Sep 7, 2014 at 7:49 pm
    How does it determine what to return if you aren't using named return
    values?
      On 8/09/2014 6:35 AM, "Don Garrett" wrote:

    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less
    text:

    The @ symbol prepended before a named variable in the return of list of
    current method (ErrorPassAlonger's) return list:

    func ErrorPassAlonger() (value string, err Error) {
    @err = GenerateError()

    ...
    }

    Which expands to the following:

    func ErrorPassAlonger() (value string, err Error) {
    err = GenerateError()
    if err != nil {
    return
    }
    }

    This could easily work with multiple assignments on the left hand side:

    a, b, @err := DoSomething()

    And, because it is defined to use a naked return, it works for multiple
    return values from ErrorPassAlonger.

    An alternative that makes it more clear that this is about error handling
    is to define the @ expansion as returning all nil values, other than the
    designated value.

    We could also say that a naked @ in an assignment list is the equivalent
    of @err:
    @ = GenerateError()


    PS: I'm not sure this is a super-awesome-must-implement proposal, but I'd
    really like SOME way of reducing the amount of dumb error handling boiler
    plate in my code.

    --
    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+unsubscribe@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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Tahir at Sep 7, 2014 at 8:52 pm
    It is an interesting idea .
    I might have misunderstood but I am not sure I like the idea of not seeing/
    not declaring explicitly what I return.
    On Sunday, September 7, 2014 7:35:18 PM UTC+1, Don Garrett wrote:

    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less
    text:

    The @ symbol prepended before a named variable in the return of list of
    current method (ErrorPassAlonger's) return list:

    func ErrorPassAlonger() (value string, err Error) {
    @err = GenerateError()

    ...
    }

    Which expands to the following:

    func ErrorPassAlonger() (value string, err Error) {
    err = GenerateError()
    if err != nil {
    return
    }
    }

    This could easily work with multiple assignments on the left hand side:

    a, b, @err := DoSomething()

    And, because it is defined to use a naked return, it works for multiple
    return values from ErrorPassAlonger.

    An alternative that makes it more clear that this is about error handling
    is to define the @ expansion as returning all nil values, other than the
    designated value.

    We could also say that a naked @ in an assignment list is the equivalent
    of @err:
    @ = GenerateError()


    PS: I'm not sure this is a super-awesome-must-implement proposal, but I'd
    really like SOME way of reducing the amount of dumb error handling boiler
    plate in my code.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Dmitri Shuralyov at Sep 7, 2014 at 9:16 pm
    I've been thinking about something like this too, so I am in support of the
    idea. I don't know about the implementation details yet.

    It could be done outside of the language with the help of an independent
    goimports-like tool.

    On Sunday, September 7, 2014 1:52:27 PM UTC-7, Tahir wrote:

    It is an interesting idea .
    I might have misunderstood but I am not sure I like the idea of not
    seeing/ not declaring explicitly what I return.
    On Sunday, September 7, 2014 7:35:18 PM UTC+1, Don Garrett wrote:

    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less
    text:

    The @ symbol prepended before a named variable in the return of list of
    current method (ErrorPassAlonger's) return list:

    func ErrorPassAlonger() (value string, err Error) {
    @err = GenerateError()

    ...
    }

    Which expands to the following:

    func ErrorPassAlonger() (value string, err Error) {
    err = GenerateError()
    if err != nil {
    return
    }
    }

    This could easily work with multiple assignments on the left hand side:

    a, b, @err := DoSomething()

    And, because it is defined to use a naked return, it works for multiple
    return values from ErrorPassAlonger.

    An alternative that makes it more clear that this is about error handling
    is to define the @ expansion as returning all nil values, other than the
    designated value.

    We could also say that a naked @ in an assignment list is the equivalent
    of @err:
    @ = GenerateError()


    PS: I'm not sure this is a super-awesome-must-implement proposal, but I'd
    really like SOME way of reducing the amount of dumb error handling boiler
    plate in my code.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Dan Kortschak at Sep 7, 2014 at 10:16 pm
    This proposal in other shapes has been made a number of times. You point to motivating cases in summary, but don't provide code. Often there are existing language approaches that simplify error handling (e.g. http://talks.golang.org/2013/bestpractices.slide#5).

    On 08/09/2014, at 4:05 AM, "Don Garrett" wrote:

    A really hefty percentage of my code consists of:

    if err != nil {
         return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less text:

    The @ symbol prepended before a named variable in the return of list of current method (ErrorPassAlonger's) return list:

    func ErrorPassAlonger() (value string, err Error) {
         @err = GenerateError()

         ...
    }

    Which expands to the following:

    func ErrorPassAlonger() (value string, err Error) {
         err = GenerateError()
         if err != nil {
             return
         }
    }

    This could easily work with multiple assignments on the left hand side:

    a, b, @err := DoSomething()

    And, because it is defined to use a naked return, it works for multiple return values from ErrorPassAlonger.

    An alternative that makes it more clear that this is about error handling is to define the @ expansion as returning all nil values, other than the designated value.

    We could also say that a naked @ in an assignment list is the equivalent of @err:
         @ = GenerateError()


    PS: I'm not sure this is a super-awesome-must-implement proposal, but I'd really like SOME way of reducing the amount of dumb error handling boiler plate in my code.


    --
    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+unsubscribe@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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Don Garrett at Sep 8, 2014 at 6:08 am
    (Replying a second time to all, with additional content).

    I started looking through my current project, and found something
    interesting. Intuitively, I expected to find a lot examples in which the
    boiler plate error handling lines exceeded the rest of the lines. I did
    find some, but not nearly as many as I was expecting, and the majority of
    the ones I found were short.

    It may well be that I'm writing a lot of methods with excessive boiler
    plate, them reducing it as I get them closer to finished.

    A common short example (6 boiler > 4 non-boiler):

    func (s *Status) GetSubStatus(url string) (contents *Status, revision int,
    e error) {
    value, revision, e := s.Get(url)
    if e != nil {
    return nil, 0, e
    }

    contents = &Status{}
    e = contents.Set("status://", value, 0)
    if e != nil {
    return nil, 0, e
    }

    return contents, revision, nil
    }

    Here it is with my original proposal:

    func (s *Status) GetSubStatus(url string) (contents *Status, revision int,
    e error) {
    value, revision, @e := s.Get(url)

    contents = &Status{}
    @e = contents.Set("status://", value, 0)

    return contents, revision, nil
    }



    A longer example (15 boiler, 13 non-boiler):

    func newFileAdapter(base base) (a adapter, err error) {
    filename, _ := base.config.GetString("status://filename")
    // Todo: if an error is present, verify it's for filename not existing.

    // The default file name is based on the name of the adapter.
    if filename == "" {
    filename = filepath.Base(base.adapterUrl) + ".json"
    }

    configDir, err := base.status.GetString(options.CONFIG_DIR)
    if err != nil {
    return nil, err
    }

    relative_name := filepath.Join(configDir, filename)
    abs_name, err := filepath.Abs(relative_name)
    if err != nil {
    return nil, err
    }

    watcher, err := fsnotify.NewWatcher()
    if err != nil {
    return nil, err
    }

    fa := &fileAdapter{base, abs_name, watcher}

    err = fa.loadFile()
    if err != nil {
    return nil, err
    }

    // Setup watch on file so we can reload if it's updated.
    go fa.Handler()
    err = fa.watcher.Add(fa.filename)
    if err != nil {
    return nil, err
    }

    return fa, nil
    }

    Here it is with an alternate syntax approach I'd be comfortable with:

    func newFileAdapter(base base) (a adapter, err error) {
    filename, _ := base.config.GetString("status://filename")
    // Todo: if an error is present, verify it's for filename not existing.

    // The default file name is based on the name of the adapter.
    if filename == "" {
    filename = filepath.Base(base.adapterUrl) + ".json"
    }

    configDir, err := base.status.GetString(options.CONFIG_DIR)
             handle err

    relative_name := filepath.Join(configDir, filename)
    abs_name, err := filepath.Abs(relative_name)
             handle err

    watcher, err := fsnotify.NewWatcher()
             handle err

    fa := &fileAdapter{base, abs_name, watcher}

    err = fa.loadFile()
             handle err

    // Setup watch on file so we can reload if it's updated.
    go fa.Handler()
    err = fa.watcher.Add(fa.filename)
             handle err

    return fa, nil
    }

    In this case, define handle to mean return if err is non-nil, and return
    all other values with their nil values. Not that this has the same problem
    as the original proposal if named return values aren't used.

    On Sun, Sep 7, 2014 at 3:16 PM, Dan Kortschak wrote:

    This proposal in other shapes has been made a number of times. You point
    to motivating cases in summary, but don't provide code. Often there are
    existing language approaches that simplify error handling (e.g.
    http://talks.golang.org/2013/bestpractices.slide#5).

    On 08/09/2014, at 4:05 AM, "Don Garrett" wrote:

    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less
    text:

    The @ symbol prepended before a named variable in the return of list of
    current method (ErrorPassAlonger's) return list:

    func ErrorPassAlonger() (value string, err Error) {
    @err = GenerateError()

    ...
    }

    Which expands to the following:

    func ErrorPassAlonger() (value string, err Error) {
    err = GenerateError()
    if err != nil {
    return
    }
    }

    This could easily work with multiple assignments on the left hand side:

    a, b, @err := DoSomething()

    And, because it is defined to use a naked return, it works for multiple
    return values from ErrorPassAlonger.

    An alternative that makes it more clear that this is about error
    handling is to define the @ expansion as returning all nil values, other
    than the designated value.

    We could also say that a naked @ in an assignment list is the equivalent
    of @err:
    @ = GenerateError()


    PS: I'm not sure this is a super-awesome-must-implement proposal, but
    I'd really like SOME way of reducing the amount of dumb error handling
    boiler plate in my code.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.

    --
    Don

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Eric Johnson at Sep 8, 2014 at 5:33 pm
    I'm still relatively new to Go, but I think there are a number of ways I've
    dealt with this question.

    The single biggest objection I have to this proposal boils down to
    code-coverage analysis. If you don't have the conditional block, you cannot
    see, when you do code coverage analysis of your unit tests, whether or not
    you're exercising the error handling clause.

    Some reflections below:
    On Sunday, September 7, 2014 11:08:42 PM UTC-7, Don Garrett wrote:

    (Replying a second time to all, with additional content).

    I started looking through my current project, and found something
    interesting. Intuitively, I expected to find a lot examples in which the
    boiler plate error handling lines exceeded the rest of the lines. I did
    find some, but not nearly as many as I was expecting, and the majority of
    the ones I found were short.

    It may well be that I'm writing a lot of methods with excessive boiler
    plate, them reducing it as I get them closer to finished.

    A common short example (6 boiler > 4 non-boiler):

    func (s *Status) GetSubStatus(url string) (contents *Status, revision int,
    e error) {
    value, revision, e := s.Get(url)
    if e != nil {
    return nil, 0, e
    }
    I try to use the "if ______ ; err != nil { ..." construct. Saves one line
    of vertical space.

    contents = &Status{}
    e = contents.Set("status://", value, 0)
    if e != nil {
    return nil, 0, e
    }
    You've pushed functionality of the "status" object to the callers. Why not
    have a "NewStatus()" method? Why is the "Set" method failing?
    That seems odd. Rather than setting a value, it might be doing something?
    In any case, the "if" seems suspicious to me, and suggests something might
    not be what it seems. So it is useful to highlight that with the "extra"
    syntax.

    return contents, revision, nil
    }

    Here it is with my original proposal:

    func (s *Status) GetSubStatus(url string) (contents *Status, revision int,
    e error) {
    value, revision, @e := s.Get(url)

    contents = &Status{}
    @e = contents.Set("status://", value, 0)

    return contents, revision, nil
    }



    A longer example (15 boiler, 13 non-boiler):

    func newFileAdapter(base base) (a adapter, err error) {
    filename, _ := base.config.GetString("status://filename")
    // Todo: if an error is present, verify it's for filename not existing.

    // The default file name is based on the name of the adapter.
    if filename == "" {
    filename = filepath.Base(base.adapterUrl) + ".json"
    }

    configDir, err := base.status.GetString(options.CONFIG_DIR)
    if err != nil {
    return nil, err
    }
    I would combine these lines...

    relative_name := filepath.Join(configDir, filename)
    abs_name, err := filepath.Abs(relative_name)
    if err != nil {
    return nil, err
    }

    watcher, err := fsnotify.NewWatcher()
    if err != nil {
    return nil, err
    }
    ... Into a method. That way, your overall method gets shorter, the function
    is clearer, and there's less boilerplate in your function.


    fa := &fileAdapter{base, abs_name, watcher}

    err = fa.loadFile()
    if err != nil {
    Looks like you didn't close your notifier in case of error. Good thing the
    "if" block is there to let you do that work.... ;-)

    return nil, err
    }

    // Setup watch on file so we can reload if it's updated.
    go fa.Handler()
    err = fa.watcher.Add(fa.filename)
    if err != nil {
    return nil, err
    }

    return fa, nil
    }
    I've found several ways to think about reducing the boilerplate:

        - Use the "if _____ ; err != nil { approach - I care more about the
        vertical space for readability.
        - If I've got a sequence of methods to call that are closely related,
        I'll pass the error to each method, and it will simply return from the
        called method if there's already an error.
        - Separate out code that can trigger errors from those that cannot.
        - Move code into methods, so the overall boilerplate goes down - and in
        fact, the signature of the extract method changes to return something
        different (thus changing the "boilerplate").
        - Table/list-driven code - create a coordinating function that invokes
        multiple other functions, and skips the latter methods if an error occurs.
        - Augmenting the "error" being returned, so the if err construct is no
        longer boilerplate

    I've come around to thinking that I'm better off thinking hard about these
    problems, rather than thinking of the constructs as boilerplate. Sometimes
    they're routine, but that's a useful signal to the reader of your code.

    Eric.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Harmen B at Sep 8, 2014 at 6:01 pm



    - Use the "if _____ ; err != nil { approach - I care more about the
    vertical space for readability.

    Try a reverse-if, the Perl way:
    return nil, err if err != nil;

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Dan Kortschak at Sep 8, 2014 at 11:17 pm

    On Mon, 2014-09-08 at 20:00 +0200, Harmen B wrote:
    return nil, err if err != nil;
    I propose we all the non-primary verbs at the end of the sentence put.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Jérôme Champion at Sep 8, 2014 at 7:12 am
    I think the problem could be solved within the code editor with two feature:
    *The first one would be for the code editor to generate the common snippet
    of code to handle the selected error variable:
    - you select the err variable
    - press a short cut and the err code handling is inserted the line after.

    *The second would be a way to reduce/expand every error condition with a
    simple short cut.

    if err != nil {
         return err
    }

    would reduce in

    if err != nil { [...] }


    Le dimanche 7 septembre 2014 20:35:18 UTC+2, Don Garrett a écrit :
    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less
    text:

    The @ symbol prepended before a named variable in the return of list of
    current method (ErrorPassAlonger's) return list:

    func ErrorPassAlonger() (value string, err Error) {
    @err = GenerateError()

    ...
    }

    Which expands to the following:

    func ErrorPassAlonger() (value string, err Error) {
    err = GenerateError()
    if err != nil {
    return
    }
    }

    This could easily work with multiple assignments on the left hand side:

    a, b, @err := DoSomething()

    And, because it is defined to use a naked return, it works for multiple
    return values from ErrorPassAlonger.

    An alternative that makes it more clear that this is about error handling
    is to define the @ expansion as returning all nil values, other than the
    designated value.

    We could also say that a naked @ in an assignment list is the equivalent
    of @err:
    @ = GenerateError()


    PS: I'm not sure this is a super-awesome-must-implement proposal, but I'd
    really like SOME way of reducing the amount of dumb error handling boiler
    plate in my code.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Francisco Dalla Rosa Soares at Sep 8, 2014 at 9:37 am

    On Mon, Sep 8, 2014 at 3:35 AM, Don Garrett wrote:

    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }
    I haven't been in this list for very long but I think I've already seen
    this come up like, a hundred times.
    Here's a solution given by Rob Pike in a different thread a few days ago

    x, err := somethingThatMightFail()
    check(err)

    and you just make that check function do exactly what your repeated if
    statement is doing :)

    func check (err Error) {
       if err != nil {
         return err
       }
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less
    text:

    The @ symbol prepended before a named variable in the return of list of
    current method (ErrorPassAlonger's) return list:

    func ErrorPassAlonger() (value string, err Error) {
    @err = GenerateError()

    ...
    }

    Which expands to the following:

    func ErrorPassAlonger() (value string, err Error) {
    err = GenerateError()
    if err != nil {
    return
    }
    }

    This could easily work with multiple assignments on the left hand side:

    a, b, @err := DoSomething()

    And, because it is defined to use a naked return, it works for multiple
    return values from ErrorPassAlonger.

    An alternative that makes it more clear that this is about error handling
    is to define the @ expansion as returning all nil values, other than the
    designated value.

    We could also say that a naked @ in an assignment list is the equivalent
    of @err:
    @ = GenerateError()


    PS: I'm not sure this is a super-awesome-must-implement proposal, but I'd
    really like SOME way of reducing the amount of dumb error handling boiler
    plate in my code.

    --
    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+unsubscribe@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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Webuser1200 at Sep 8, 2014 at 11:42 pm
    +1
    On Sunday, September 7, 2014 2:35:18 PM UTC-4, Don Garrett wrote:

    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less
    text:

    The @ symbol prepended before a named variable in the return of list of
    current method (ErrorPassAlonger's) return list:

    func ErrorPassAlonger() (value string, err Error) {
    @err = GenerateError()

    ...
    }

    Which expands to the following:

    func ErrorPassAlonger() (value string, err Error) {
    err = GenerateError()
    if err != nil {
    return
    }
    }

    This could easily work with multiple assignments on the left hand side:

    a, b, @err := DoSomething()

    And, because it is defined to use a naked return, it works for multiple
    return values from ErrorPassAlonger.

    An alternative that makes it more clear that this is about error handling
    is to define the @ expansion as returning all nil values, other than the
    designated value.

    We could also say that a naked @ in an assignment list is the equivalent
    of @err:
    @ = GenerateError()


    PS: I'm not sure this is a super-awesome-must-implement proposal, but I'd
    really like SOME way of reducing the amount of dumb error handling boiler
    plate in my code.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Uli Kunitz at Sep 9, 2014 at 8:13 pm
    If I read complains about repetitive codes I always wonder if people don't
    know their editors well enough.

    Some options one can use in vim to use it.

    1) line completion ^X^L (repeat ^X^L for next line)
    2) map a key
    3) use abbreviations

    There will be definitely a lot of other options.







    On Sunday, September 7, 2014 8:35:18 PM UTC+2, Don Garrett wrote:

    A really hefty percentage of my code consists of:

    if err != nil {
    return err
    }

    I'm proposing a shortcut that maintains equivalent semantics, but less
    text:

    The @ symbol prepended before a named variable in the return of list of
    current method (ErrorPassAlonger's) return list:

    func ErrorPassAlonger() (value string, err Error) {
    @err = GenerateError()

    ...
    }

    Which expands to the following:

    func ErrorPassAlonger() (value string, err Error) {
    err = GenerateError()
    if err != nil {
    return
    }
    }

    This could easily work with multiple assignments on the left hand side:

    a, b, @err := DoSomething()

    And, because it is defined to use a naked return, it works for multiple
    return values from ErrorPassAlonger.

    An alternative that makes it more clear that this is about error handling
    is to define the @ expansion as returning all nil values, other than the
    designated value.

    We could also say that a naked @ in an assignment list is the equivalent
    of @err:
    @ = GenerateError()


    PS: I'm not sure this is a super-awesome-must-implement proposal, but I'd
    really like SOME way of reducing the amount of dumb error handling boiler
    plate in my code.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.

Related Discussions

People

Translate

site design / logo © 2022 Grokbase