FAQ
Hi all,

I am interested in error-related changes. Frequently I want to check for an
error, and pass it 'up' if it occurs:

_, err := someFn()
if nil!=err { return err }

I've tidied that up a little to

if _,err :=someFn(); nil!=err {
return err
}

This almost becomes boilerplate, and causes a lot of tedious repetition and
duplication. If I change my function return signature, I have to duplicate
this at each point.

I would like to have something like this:

func inFix(err error) (string,error) {
return "", err
}

func GenerateError() (string,error) {
return "", errors.New("My error")
}

func One() (string,error) {
x, ^inFix := GenerateError()

return x, nil
}

The rules are simple:

r1. A 'hat' function can take only a single parameter, an interface{} or a
pointer - anything that can be nil, and MUST return the same values as the
function in which it is called.

r2. It is a compilation error to ^ a hat function within a containing
function that has a different return signature.
In my example, it would be a compilation error to use ^ inFix function
in a function returning (int, error)

r3. If a ^ appears in an assignment position, it must be in the position of
an interface return value that matches the parameter of the hat function.
In my example, ^inFix occurs at the position where 'error' is being
returned. Since inFix takes an error parameter, this matches. Anything else
would be a compilation error.

r4. If the parameter returned by the function in the position of the ^ call
is not-nil, the hat function is called, and the return values of the hat
function are returned by the containing function.

Implementation notes:

i1. A crude compilation:

x, ^h := f(x)

expands to:

var err_h error
// define x if it's not already defined
if x,err_h = f(x); nil!=err_h {
return h(err_h)
}

Observations:

o1. The ^ in the syntax is not obviously necessary. However, it does
prevent ambiguity in the case of := where the first variable is not defined:
x, inFix := GenerateError()
This clearly means define 'x', but does it mean define a local variable
inFix of type error, or use the global inFix(error) function as a hat
function? ^ removes the ambiguity.

o2. I've not thought of a way to make this even more generic. At this level
of definition, a hat function can take only one nil-able parameter. Might
one think of something more flexible?

Finally:

I'd be very interested if more experienced Gopher's tell me I'm doing
errors all wrong, or that I'm messing up if I'm just passing errors 'up'
the stack. Please don't hesitate to criticize.

All the best,
Craig

--
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/groups/opt_out.

Search Discussions

  • Volker Dobler at Apr 5, 2013 at 7:58 am

    Am Freitag, 5. April 2013 09:31:11 UTC+2 schrieb Craig Mason-Jones:
    I am interested in error-related changes. Frequently I want to check for
    an error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition
    and duplication. If I change my function return signature, I have to
    duplicate this at each point.

    That is exactly the way to go. It isn't considered "boilerplate" but
    "error handling".

    I would like to have something like this:
    This won't happen the next years. Anyway: You will get used to what you
    think of as "boilerplate".

    V.

    --
    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/groups/opt_out.
  • Bryan Matsuo at Apr 5, 2013 at 9:15 am
    if err != nil {
    return err
    }

    This can be the proper thing to do in some cases. But in many cases it is
    really just the lazy thing to do when instead a different error with a
    message appropriate for the given context can be returned. Enforcing this
    pattern with a simplified syntax promotes unhelpful error messages.
    On Friday, April 5, 2013 12:58:13 AM UTC-7, Volker Dobler wrote:


    Am Freitag, 5. April 2013 09:31:11 UTC+2 schrieb Craig Mason-Jones:
    I am interested in error-related changes. Frequently I want to check for
    an error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition
    and duplication. If I change my function return signature, I have to
    duplicate this at each point.

    That is exactly the way to go. It isn't considered "boilerplate" but
    "error handling".

    I would like to have something like this:
    This won't happen the next years. Anyway: You will get used to what you
    think of as "boilerplate".

    V.
    --
    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/groups/opt_out.
  • Dario at Apr 6, 2013 at 12:57 am
    2013/4/5 Volker Dobler <dr.volker.dobler@gmail.com>
    Am Freitag, 5. April 2013 09:31:11 UTC+2 schrieb Craig Mason-Jones:
    I am interested in error-related changes. Frequently I want to check for
    an error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition
    and duplication. If I change my function return signature, I have to
    duplicate this at each point.

    That is exactly the way to go. It isn't considered "boilerplate" but
    "error handling".
    I don't mean to flame. Yes, it is "error handling" but...

    I would like to have something like this:
    This won't happen the next years. Anyway: You will get used to what you
    think of as "boilerplate".
    ... when you need to repeat your intention, it is boilerplate. [0] Anyway,
    there is no need to be harsh.

    I almost like this proposal. I'm still a Go newbie (3fps already suffered
    me - sorry!) but I got the same idea (even with that syntax) a few weeks
    ago. Go has a very nice syntax and this is the only thing I miss.

    Having a lot of functions returning errors tends to clutter code with "if
    err != nil" statements here and there. Just a sample from my own code:

    func (gen *Generator) handleMIMETypePlugin(e xml.Node, doc
    *html.HtmlDocument) (err error) {
    src := e.Attribute("src").Value()
    typ := e.Attribute("type").Value()
    cmd := exec.Command(fmt.Sprintf("m%s%s", ZNG_PREFIX,
    gen.resolveMIMETypePlugin(typ)), src)
    stdout, err := cmd.StdoutPipe()
    if err != nil {
    return err
    }
    cmd.Stderr = os.Stderr
    c := make(chan bufErr)
    go func() {
    data, err := ioutil.ReadAll(stdout)
    c <- bufErr{data, err}
    }()
    if err = cmd.Start(); err != nil {
    return err
    }
    be := <-c
    if err = cmd.Wait(); err != nil {
    return err
    }
    if be.err != nil {
    return be.err
    }
    parent := e.Parent()
    child, err := doc.Coerce(be.buffer)
    if err != nil {
    return err
    }
    parent.AddChild(child)
    e.Remove()
    return
    }

    Maybe this is bad code but I don't see a better alternative for what I'm
    doing (calling an external command and piping its output to a buffer, later
    adding it to a Gokogiri node). How it would look with ^?

    func (gen *Generator) handleMIMETypePlugin(e xml.Node, doc
    *html.HtmlDocument) (err error) {
    src := e.Attribute("src").Value()
    typ := e.Attribute("type").Value()
    cmd := exec.Command(fmt.Sprintf("m%s%s", ZNG_PREFIX,
    gen.resolveMIMETypePlugin(typ)), src)
    stdout, ^err := cmd.StdoutPipe()
    cmd.Stderr = os.Stderr
    c := make(chan bufErr)
    go func() {
    data, err := ioutil.ReadAll(stdout)
    c <- bufErr{data, err}
    }()
    ^err = cmd.Start()
    be := <-c
    ^err = cmd.Wait()
    if be.err != nil {
    return be.err
    }
    parent := e.Parent()
    child, ^err := doc.Coerce(be.buffer)
    parent.AddChild(child)
    e.Remove()
    return
    }

    It looks good! It is shorter but I agree that this breaks "Go's spirit".
    Here we have two kinds of "return": normal and shortened for "if this is
    null, return". It can be confusing for any newcomer ("Wait, why this method
    returns an error here if you only have one return at the end?").

    So, should we keep adding "if err != nil { return err }" forever? Unless we
    add an specific meaningful keyword or construct for this common situation,
    yes, we should.

    [0] http://en.wikipedia.org/wiki/Boilerplate_code

    --
    Dario Castañé
    http://www.dario.im | http://twitter.com/im_dario

    --
    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/groups/opt_out.
  • Volker Dobler at Apr 6, 2013 at 6:12 am
    This topic has been discussed in endless threads in this mailing list
    already. It is always some variant of
    "I do not want to write if err != nil { return err }"
    with suggesting either exceptions or some clever syntax change
    which does if err != nil { return err } automatically.
    This horse is dead.
    It won't happen.

    V.

    Am Freitag, 5. April 2013 23:50:47 UTC+2 schrieb Dario Castañé:
    2013/4/5 Volker Dobler <dr.volke...@gmail.com <javascript:>>
    Am Freitag, 5. April 2013 09:31:11 UTC+2 schrieb Craig Mason-Jones:
    I am interested in error-related changes. Frequently I want to check for
    an error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition
    and duplication. If I change my function return signature, I have to
    duplicate this at each point.

    That is exactly the way to go. It isn't considered "boilerplate" but
    "error handling".
    I don't mean to flame. Yes, it is "error handling" but...

    I would like to have something like this:
    This won't happen the next years. Anyway: You will get used to what you
    think of as "boilerplate".
    ... when you need to repeat your intention, it is boilerplate. [0] Anyway,
    there is no need to be harsh.

    I almost like this proposal. I'm still a Go newbie (3fps already suffered
    me - sorry!) but I got the same idea (even with that syntax) a few weeks
    ago. Go has a very nice syntax and this is the only thing I miss.

    Having a lot of functions returning errors tends to clutter code with "if
    err != nil" statements here and there. Just a sample from my own code:

    func (gen *Generator) handleMIMETypePlugin(e xml.Node, doc
    *html.HtmlDocument) (err error) {
    src := e.Attribute("src").Value()
    typ := e.Attribute("type").Value()
    cmd := exec.Command(fmt.Sprintf("m%s%s", ZNG_PREFIX,
    gen.resolveMIMETypePlugin(typ)), src)
    stdout, err := cmd.StdoutPipe()
    if err != nil {
    return err
    }
    cmd.Stderr = os.Stderr
    c := make(chan bufErr)
    go func() {
    data, err := ioutil.ReadAll(stdout)
    c <- bufErr{data, err}
    }()
    if err = cmd.Start(); err != nil {
    return err
    }
    be := <-c
    if err = cmd.Wait(); err != nil {
    return err
    }
    if be.err != nil {
    return be.err
    }
    parent := e.Parent()
    child, err := doc.Coerce(be.buffer)
    if err != nil {
    return err
    }
    parent.AddChild(child)
    e.Remove()
    return
    }

    Maybe this is bad code but I don't see a better alternative for what I'm
    doing (calling an external command and piping its output to a buffer, later
    adding it to a Gokogiri node). How it would look with ^?

    func (gen *Generator) handleMIMETypePlugin(e xml.Node, doc
    *html.HtmlDocument) (err error) {
    src := e.Attribute("src").Value()
    typ := e.Attribute("type").Value()
    cmd := exec.Command(fmt.Sprintf("m%s%s", ZNG_PREFIX,
    gen.resolveMIMETypePlugin(typ)), src)
    stdout, ^err := cmd.StdoutPipe()
    cmd.Stderr = os.Stderr
    c := make(chan bufErr)
    go func() {
    data, err := ioutil.ReadAll(stdout)
    c <- bufErr{data, err}
    }()
    ^err = cmd.Start()
    be := <-c
    ^err = cmd.Wait()
    if be.err != nil {
    return be.err
    }
    parent := e.Parent()
    child, ^err := doc.Coerce(be.buffer)
    parent.AddChild(child)
    e.Remove()
    return
    }

    It looks good! It is shorter but I agree that this breaks "Go's spirit".
    Here we have two kinds of "return": normal and shortened for "if this is
    null, return". It can be confusing for any newcomer ("Wait, why this method
    returns an error here if you only have one return at the end?").

    So, should we keep adding "if err != nil { return err }" forever? Unless
    we add an specific meaningful keyword or construct for this common
    situation, yes, we should.

    [0] http://en.wikipedia.org/wiki/Boilerplate_code

    --
    Dario Castañé
    http://www.dario.im | http://twitter.com/im_dario
    --
    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/groups/opt_out.
  • Toni Cárdenas at Apr 5, 2013 at 9:22 am
    I don't particularly like your proposal but I do think the approach "handle
    every error by copypasting the same if block below every function call" is
    a common pattern that can get annoying. So I'm using this little
    function: http://play.golang.org/p/plIr3nkYsE and I'm happier while not
    adding any overhead to the language. (I strongly fear exceptions.)
    On Friday, April 5, 2013 9:31:11 AM UTC+2, Craig Mason-Jones wrote:

    Hi all,

    I am interested in error-related changes. Frequently I want to check for
    an error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition
    and duplication. If I change my function return signature, I have to
    duplicate this at each point.

    I would like to have something like this:

    func inFix(err error) (string,error) {
    return "", err
    }

    func GenerateError() (string,error) {
    return "", errors.New("My error")
    }

    func One() (string,error) {
    x, ^inFix := GenerateError()

    return x, nil
    }

    The rules are simple:

    r1. A 'hat' function can take only a single parameter, an interface{} or a
    pointer - anything that can be nil, and MUST return the same values as the
    function in which it is called.

    r2. It is a compilation error to ^ a hat function within a containing
    function that has a different return signature.
    In my example, it would be a compilation error to use ^ inFix function
    in a function returning (int, error)

    r3. If a ^ appears in an assignment position, it must be in the position
    of an interface return value that matches the parameter of the hat function.
    In my example, ^inFix occurs at the position where 'error' is being
    returned. Since inFix takes an error parameter, this matches. Anything else
    would be a compilation error.

    r4. If the parameter returned by the function in the position of the ^
    call is not-nil, the hat function is called, and the return values of the
    hat function are returned by the containing function.

    Implementation notes:

    i1. A crude compilation:

    x, ^h := f(x)

    expands to:

    var err_h error
    // define x if it's not already defined
    if x,err_h = f(x); nil!=err_h {
    return h(err_h)
    }

    Observations:

    o1. The ^ in the syntax is not obviously necessary. However, it does
    prevent ambiguity in the case of := where the first variable is not defined:
    x, inFix := GenerateError()
    This clearly means define 'x', but does it mean define a local variable
    inFix of type error, or use the global inFix(error) function as a hat
    function? ^ removes the ambiguity.

    o2. I've not thought of a way to make this even more generic. At this
    level of definition, a hat function can take only one nil-able parameter.
    Might one think of something more flexible?

    Finally:

    I'd be very interested if more experienced Gopher's tell me I'm doing
    errors all wrong, or that I'm messing up if I'm just passing errors 'up'
    the stack. Please don't hesitate to criticize.

    All the best,
    Craig
    --
    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/groups/opt_out.
  • Ewan Chou at Apr 5, 2013 at 9:50 am
    In my application I use "panic" with deferred "recover" extensively to
    avoid the boilerplate.

    If the error I get from a deeper function make the whole operation fail, I
    will call panic.

    But panic should not be called without care, I only call panic with the
    following conditions fulfilled:
    1) Make sure the outer method have a deferred recover catch the panic.
    2) Wrap the error with an application specific error interface when panic,
    like "panic(NewCustomError(err))", which can be inferred in the recover
    method.

    In the recover method, I do type assertion, and handle the error based on
    error's type, and the error's extra information that I defined when panic
    it.

    This approach allows you to centralize the error handling code.

    Especially useful when errors generated in many place needs to be handled
    in same way.

    --
    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/groups/opt_out.
  • Craig Mason-Jones at Apr 6, 2013 at 12:00 pm
    Thank you everyone for the interesting replies to this thread. I come away
    with a few thoughts:

    t1. A new syntax doesn't promote unhelpful error messages. Go already
    permits a worse alternative:

    db, _ := sql.Open(driver, connectionString)

    A new syntax _discourages_ the worse alternative, because it makes a
    better alternative easier.

    t2. The ^ proposal provides something more functional than a shorthand for
    `if nil!=err { return err }`. An example:

    func RequestHandler(w http.ResponseWriter, r *http.Request) {

    errHandler := func(err error) {
    log.Println(err.Error())
    http.Error(w, err.Error(), http.StatusInternalServerError)
    }

    db, ^errHandler := sql.Open( ... )
    rows, ^errHandler := db.Query("select name from ...")
    for rows.Next() {
    var name string
    ^errHandler = rows.Scan(&name)
    ...
    }
    temp, ^errHandler := templates.ParseFiles("test.html")
    ^errHandler := temp.Execute(w, ...)
    }

    This is a situation where the value of the ^ proposal shines.

    t3. The ^ syntax is (slightly smart) syntactic sugar. It's not introducing
    any new or radical ideas to the language, and no changes are required to
    the runtime.
    It is slightly smarter than syntactic sugar:

    x, ^h := f()

    Needs to 'expand' to:

    // iff _h_param not previously defined for ^h, where _h_param is
    scope-unique,
    // and HP is the parameter type of h
    var _h_param HP
    x, _h_param = f()
    if nil!=_h_param {
    h(_h_param)
    }

    On the other hand:

    x, ^h = f()

    Needs to include `var _h_param HP` only if _h_param hasn't yet been
    defined for ^h. Of course, in a naive implementation one could simply
    always define a unique new HP var for each ^ call.

    It needs to be slightly 'context aware' syntactic sugar.

    t4. There is a suggestion to use `panic` instead of `return err`. I don't
    like this because:

    t4.1 Library functions seldom panic, so I can only use such an approach
    in my own code, for which I again need `if nil!=err { panic(err) }`

    t4.2 This is exceptions by workaround. I defer to the opinion of the Go
    designers, and the 'deal with the error at the point of error' approach.
    Using `panic()` subverts that ethos.

    t5. The tryCatch(func, func) approach (http://play.golang.org/p/plIr3nkYsE)
    provides a short hand but doesn't permit returning values from functions. I
    can't write:

    db := tryCatch(sql.Open(...), func())

    The solution is very limited (unless I've misunderstood it).

    I also think it suffers from separating the point where the error
    occurs and the point where it is handled (in the 'catch' function).

    t6. Any solution should be idiomatic. In particular, it should support := .
    This is my big 'issue' with a tryCatch() function, and an issue I have as
    well with if ... {} format.

    One might like to write:

    if db, err := sql.Open(..); nil!=err { ... }

    That doesn't work, since the scope of db is inside the if clause.

    Idiomatic must be:

    db, err := sql.Open(...)
    if nil!=err { ... }

    On the other hand, the ^ syntax becomes, very naturally:

    db, ^errFn := sql.Open(...)

    t7. Dr Volker points out this has been discussed in endless threads.

    It is an itch that needs scratching.

    ====

    In conclusion, consider this code from
    http://golang.org/src/pkg/net/http/request.go, starting line 338:

    338 // Process Body,ContentLength,Close,Trailer
    339 tw, err := newTransferWriter(req)
    340 if err != nil {
    341 return err
    342 }
    343 err = tw.WriteHeader(bw)
    344 if err != nil {
    345 return err
    346 }
    347
    348 // TODO: split long values? (If so, should share code with
    Conn.Write)
    349 err = req.Header.WriteSubset(bw, reqWriteExcludeHeader)
    350 if err != nil {
    351 return err
    352 }
    353
    354 if extraHeaders != nil {
    355 err = extraHeaders.Write(bw)
    356 if err != nil {
    357 return err
    358 }
    359 }
    360
    361 io.WriteString(bw, "\r\n")
    362
    363 // Write body and trailer
    364 err = tw.WriteBody(bw)
    365 if err != nil {
    366 return err
    367 }

    Rewritten with ^:

    338 errF := func(e error) error { // ^ only called if param != nil
    339 return e
    340 }
    341
    342 // Process Body,ContentLength,Close,Trailer
    343 tw, ^errF := newTransferWriter(req)
    344 ^errF = tw.WriteHeader(bw)
    345
    346 // TODO: split long values? (If so, should share code with
    Conn.Write)
    347 ^errF = req.Header.WriteSubset(bw, reqWriteExcludeHeader)
    348
    349 if extraHeaders != nil {
    350 ^errF = extraHeaders.Write(bw)
    351 }
    352
    353 _, ^errF = io.WriteString(bw, "\r\n")
    354
    355 // Write body and trailer
    356 ^errF = tw.WriteBody(bw)

    I'm catching one error more than the original code (io.WriteString on
    361 in original code, 355 in ^ code), and have reduced 30 lines to 19 (ok,
    I cherry-picked the example, but I cherry-picked it from the Go source).

    --
    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/groups/opt_out.
  • Jesse McNelis at Apr 6, 2013 at 12:11 pm

    On Sat, Apr 6, 2013 at 11:00 PM, Craig Mason-Jones wrote:

    Rewritten with ^:

    338 errF := func(e error) error { // ^ only called if param != nil
    339 return e
    340 }
    This function doesn't do anything. I don't see the point of it.
    If the '^' operator is supposed to pass the value to the named function if
    it's not nil then you still don't have anything to change the flow of the
    program. eg. How does this help you return the error to the caller?


    342 // Process Body,ContentLength,Close,Trailer
    343 tw, ^errF := newTransferWriter(req)
    344 ^errF = tw.WriteHeader(bw)
    345
    346 // TODO: split long values? (If so, should share code with
    Conn.Write)
    347 ^errF = req.Header.WriteSubset(bw, reqWriteExcludeHeader)
    348
    349 if extraHeaders != nil {
    350 ^errF = extraHeaders.Write(bw)
    351 }
    352
    353 _, ^errF = io.WriteString(bw, "\r\n")
    354
    355 // Write body and trailer
    356 ^errF = tw.WriteBody(bw)

    I'm catching one error more than the original code (io.WriteString on
    361 in original code, 355 in ^ code), and have reduced 30 lines to 19 (ok,
    I cherry-picked the example, but I cherry-picked it from the Go source).


    --
    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/groups/opt_out.



    --
    =====================
    http://jessta.id.au

    --
    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/groups/opt_out.
  • Craig Mason-Jones at Apr 6, 2013 at 12:21 pm
    Sorry - I messed up the 'expansion' in my last post.

    The point is that the ^ call will return _from the calling function_, so
    (in the correct expansion):

    db, ^errF := sql.Open(...)

    becomes

    db, _temp_err := sql.Open(...)
    if nil!=_temp_err {
    return errF(_temp_err)
    }

    So the return from the ^ function becomes the return from the calling
    function.

    All the best,
    C
    On Saturday, April 6, 2013 2:11:02 PM UTC+2, Jesse McNelis wrote:

    On Sat, Apr 6, 2013 at 11:00 PM, Craig Mason-Jones <cr...@lateral.co.za<javascript:>
    wrote:
    Rewritten with ^:

    338 errF := func(e error) error { // ^ only called if param !=
    nil
    339 return e
    340 }
    This function doesn't do anything. I don't see the point of it.
    If the '^' operator is supposed to pass the value to the named function if
    it's not nil then you still don't have anything to change the flow of the
    program. eg. How does this help you return the error to the caller?
    --
    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/groups/opt_out.
  • Jan Mercl at Apr 6, 2013 at 12:31 pm

    On Sat, Apr 6, 2013 at 2:21 PM, Craig Mason-Jones wrote:
    Sorry - I messed up the 'expansion' in my last post.
    Sorry - you're probably wasting (not only) your time.

    -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/groups/opt_out.
  • Craig Mason-Jones at Apr 6, 2013 at 12:12 pm
    Sorry, just two corrections to my previous post:

    t3. I had forgotten the 'return' in the expansion. Another example of the
    'context awareness' required by the ^, which needs to become either:
    if nil!=_h_param {
    return h(_h_param)
    }

    OR, if h() returns no values:

    if nil!=_h_param {
    h(_h_param)
    return
    }

    So:

    x, ^h := f()

    Needs to 'expand' to:

    // iff _h_param not previously defined for ^h, where _h_param is
    scope-unique,
    // and HP is the parameter type of h
    var _h_param HP
    x, _h_param = f()
    if nil!=_h_param {
    return h(_h_param)
    // OR, if _h_param doesn't return any values
    h(_h_param)
    return
    }

    And in the rewrite of the code from net/http/request.go, io.WriteString is
    on line 353 of the ^ version.

    Thanks again,
    C

    --
    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/groups/opt_out.
  • Toni Cárdenas at Apr 6, 2013 at 1:16 pm
    I think you did misunderstood tryCatch.
    db := tryCatch(sql.Open(...), func())
    That's invalid. The first argument to tryCatch must be a «func (tryFunc)»,
    the «try block». Like:

    tryCatch(func (handle tryFunc) {
    db, err := sql.Open(...)
    handle(err)
    ...
    }, func (err error) bool { ... })

    Or, if you want to use db outside the «try block»:

    var db *sql.DB
    err := tryCatch(func (handle tryFunc) {
    var err error
    db, err = sql.Open(...)
    handle(err)
    ...
    }, func (err error) bool { ... })
    // Now you still have db and can deal with err if tryCatch failed to do so.
    I also think it suffers from separating the point where the error
    occurs and the point where it is handled (in the 'catch' function).

    I don't think so. You still need to call the tryFunc for each error you
    wan't to handle; it's explicit and in-place, you just don't have to
    copypaste the same block each time. And you still can use the good old if
    err != nil. It's completely orthogonal.
    On Saturday, April 6, 2013 2:00:00 PM UTC+2, Craig Mason-Jones wrote:

    Thank you everyone for the interesting replies to this thread. I come away
    with a few thoughts:

    t1. A new syntax doesn't promote unhelpful error messages. Go already
    permits a worse alternative:

    db, _ := sql.Open(driver, connectionString)

    A new syntax _discourages_ the worse alternative, because it makes a
    better alternative easier.

    t2. The ^ proposal provides something more functional than a shorthand for
    `if nil!=err { return err }`. An example:

    func RequestHandler(w http.ResponseWriter, r *http.Request) {

    errHandler := func(err error) {
    log.Println(err.Error())
    http.Error(w, err.Error(), http.StatusInternalServerError)
    }

    db, ^errHandler := sql.Open( ... )
    rows, ^errHandler := db.Query("select name from ...")
    for rows.Next() {
    var name string
    ^errHandler = rows.Scan(&name)
    ...
    }
    temp, ^errHandler := templates.ParseFiles("test.html")
    ^errHandler := temp.Execute(w, ...)
    }

    This is a situation where the value of the ^ proposal shines.

    t3. The ^ syntax is (slightly smart) syntactic sugar. It's not introducing
    any new or radical ideas to the language, and no changes are required to
    the runtime.
    It is slightly smarter than syntactic sugar:

    x, ^h := f()

    Needs to 'expand' to:

    // iff _h_param not previously defined for ^h, where _h_param is
    scope-unique,
    // and HP is the parameter type of h
    var _h_param HP
    x, _h_param = f()
    if nil!=_h_param {
    h(_h_param)
    }

    On the other hand:

    x, ^h = f()

    Needs to include `var _h_param HP` only if _h_param hasn't yet been
    defined for ^h. Of course, in a naive implementation one could simply
    always define a unique new HP var for each ^ call.

    It needs to be slightly 'context aware' syntactic sugar.

    t4. There is a suggestion to use `panic` instead of `return err`. I don't
    like this because:

    t4.1 Library functions seldom panic, so I can only use such an
    approach in my own code, for which I again need `if nil!=err { panic(err) }`

    t4.2 This is exceptions by workaround. I defer to the opinion of the
    Go designers, and the 'deal with the error at the point of error' approach.
    Using `panic()` subverts that ethos.

    t5. The tryCatch(func, func) approach (http://play.golang.org/p/plIr3nkYsE)
    provides a short hand but doesn't permit returning values from functions. I
    can't write:

    db := tryCatch(sql.Open(...), func())

    The solution is very limited (unless I've misunderstood it).

    I also think it suffers from separating the point where the error
    occurs and the point where it is handled (in the 'catch' function).

    t6. Any solution should be idiomatic. In particular, it should support :=
    . This is my big 'issue' with a tryCatch() function, and an issue I have as
    well with if ... {} format.

    One might like to write:

    if db, err := sql.Open(..); nil!=err { ... }

    That doesn't work, since the scope of db is inside the if clause.

    Idiomatic must be:

    db, err := sql.Open(...)
    if nil!=err { ... }

    On the other hand, the ^ syntax becomes, very naturally:

    db, ^errFn := sql.Open(...)

    t7. Dr Volker points out this has been discussed in endless threads.

    It is an itch that needs scratching.

    ====

    In conclusion, consider this code from
    http://golang.org/src/pkg/net/http/request.go, starting line 338:

    338 // Process Body,ContentLength,Close,Trailer
    339 tw, err := newTransferWriter(req)
    340 if err != nil {
    341 return err
    342 }
    343 err = tw.WriteHeader(bw)
    344 if err != nil {
    345 return err
    346 }
    347
    348 // TODO: split long values? (If so, should share code with
    Conn.Write)
    349 err = req.Header.WriteSubset(bw, reqWriteExcludeHeader)
    350 if err != nil {
    351 return err
    352 }
    353
    354 if extraHeaders != nil {
    355 err = extraHeaders.Write(bw)
    356 if err != nil {
    357 return err
    358 }
    359 }
    360
    361 io.WriteString(bw, "\r\n")
    362
    363 // Write body and trailer
    364 err = tw.WriteBody(bw)
    365 if err != nil {
    366 return err
    367 }

    Rewritten with ^:

    338 errF := func(e error) error { // ^ only called if param != nil
    339 return e
    340 }
    341
    342 // Process Body,ContentLength,Close,Trailer
    343 tw, ^errF := newTransferWriter(req)
    344 ^errF = tw.WriteHeader(bw)
    345
    346 // TODO: split long values? (If so, should share code with
    Conn.Write)
    347 ^errF = req.Header.WriteSubset(bw, reqWriteExcludeHeader)
    348
    349 if extraHeaders != nil {
    350 ^errF = extraHeaders.Write(bw)
    351 }
    352
    353 _, ^errF = io.WriteString(bw, "\r\n")
    354
    355 // Write body and trailer
    356 ^errF = tw.WriteBody(bw)

    I'm catching one error more than the original code (io.WriteString on
    361 in original code, 355 in ^ code), and have reduced 30 lines to 19 (ok,
    I cherry-picked the example, but I cherry-picked it from the Go source).
    --
    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/groups/opt_out.
  • Rémy Oudompheng at Apr 6, 2013 at 12:55 pm

    On 2013/4/5 Craig Mason-Jones wrote:
    Hi all,

    I am interested in error-related changes. Frequently I want to check for an
    error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition and
    duplication. If I change my function return signature, I have to duplicate
    this at each point.
    It *is* boilerplate. Writing 30 times "if err != nil { return err }"
    is not a very interesting coding style, and it is not error handling
    either.

    Using macros to hide boilerplate is not eliminating boilerplate, it's
    just giving the means to write even more boilerplate. The solution is
    not to provide an shorter way to write it 30 times. It is to either
    understand why it is not boilerplate (and keep that code), or how to
    not write it at all.

    If people just wanted to return err up the stack, they could use
    exceptions or panics. But error handling is not about pushing errors
    up the stack, it's about taking appropriate decisions and building
    helpful error messages.

    For returning errors unmodified, a few patterns, like the "sticky
    error" pattern, can help.

    Rémy.

    --
    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/groups/opt_out.
  • Toni Cárdenas at Apr 6, 2013 at 1:22 pm
    Sometimes you need just that. For instance, a helper function that does a
    number of things and just wants to stop if any of these things failed and
    have the caller deal with the error. That's a inevitable series of if err
    != nil { return err }.
    On Saturday, April 6, 2013 2:55:39 PM UTC+2, Rémy Oudompheng wrote:
    On 2013/4/5 Craig Mason-Jones <cr...@lateral.co.za <javascript:>> wrote:
    Hi all,

    I am interested in error-related changes. Frequently I want to check for an
    error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition and
    duplication. If I change my function return signature, I have to duplicate
    this at each point.
    It *is* boilerplate. Writing 30 times "if err != nil { return err }"
    is not a very interesting coding style, and it is not error handling
    either.

    Using macros to hide boilerplate is not eliminating boilerplate, it's
    just giving the means to write even more boilerplate. The solution is
    not to provide an shorter way to write it 30 times. It is to either
    understand why it is not boilerplate (and keep that code), or how to
    not write it at all.

    If people just wanted to return err up the stack, they could use
    exceptions or panics. But error handling is not about pushing errors
    up the stack, it's about taking appropriate decisions and building
    helpful error messages.

    For returning errors unmodified, a few patterns, like the "sticky
    error" pattern, can help.

    Rémy.
    --
    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/groups/opt_out.
  • Alco at Apr 7, 2013 at 9:38 am
    For returning errors unmodified, a few patterns, like the "sticky
    error" pattern, can help.

    Could you please point out a place to read about this pattern? It doesn't
    google for me.
    On Saturday, April 6, 2013 3:55:39 PM UTC+3, Rémy Oudompheng wrote:
    On 2013/4/5 Craig Mason-Jones <cr...@lateral.co.za <javascript:>> wrote:
    Hi all,

    I am interested in error-related changes. Frequently I want to check for an
    error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition and
    duplication. If I change my function return signature, I have to duplicate
    this at each point.
    It *is* boilerplate. Writing 30 times "if err != nil { return err }"
    is not a very interesting coding style, and it is not error handling
    either.

    Using macros to hide boilerplate is not eliminating boilerplate, it's
    just giving the means to write even more boilerplate. The solution is
    not to provide an shorter way to write it 30 times. It is to either
    understand why it is not boilerplate (and keep that code), or how to
    not write it at all.

    If people just wanted to return err up the stack, they could use
    exceptions or panics. But error handling is not about pushing errors
    up the stack, it's about taking appropriate decisions and building
    helpful error messages.

    For returning errors unmodified, a few patterns, like the "sticky
    error" pattern, can help.

    Rémy.
    --
    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/groups/opt_out.
  • Rémy Oudompheng at Apr 7, 2013 at 10:32 am
    See for example how bufio.Writer handles errors.

    Rémy.

    --
    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/groups/opt_out.
  • Qpliu at Apr 12, 2013 at 12:28 am
    A very similar proposal: https://gist.github.com/qpliu/3259111

    The example would be written as:

    error x, return inFix(_) := GenerateError()
    On Friday, April 5, 2013 12:31:11 AM UTC-7, Craig Mason-Jones wrote:

    Hi all,

    I am interested in error-related changes. Frequently I want to check for
    an error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition
    and duplication. If I change my function return signature, I have to
    duplicate this at each point.

    I would like to have something like this:

    func inFix(err error) (string,error) {
    return "", err
    }

    func GenerateError() (string,error) {
    return "", errors.New("My error")
    }

    func One() (string,error) {
    x, ^inFix := GenerateError()

    return x, nil
    }

    The rules are simple:

    r1. A 'hat' function can take only a single parameter, an interface{} or a
    pointer - anything that can be nil, and MUST return the same values as the
    function in which it is called.

    r2. It is a compilation error to ^ a hat function within a containing
    function that has a different return signature.
    In my example, it would be a compilation error to use ^ inFix function
    in a function returning (int, error)

    r3. If a ^ appears in an assignment position, it must be in the position
    of an interface return value that matches the parameter of the hat function.
    In my example, ^inFix occurs at the position where 'error' is being
    returned. Since inFix takes an error parameter, this matches. Anything else
    would be a compilation error.

    r4. If the parameter returned by the function in the position of the ^
    call is not-nil, the hat function is called, and the return values of the
    hat function are returned by the containing function.

    Implementation notes:

    i1. A crude compilation:

    x, ^h := f(x)

    expands to:

    var err_h error
    // define x if it's not already defined
    if x,err_h = f(x); nil!=err_h {
    return h(err_h)
    }

    Observations:

    o1. The ^ in the syntax is not obviously necessary. However, it does
    prevent ambiguity in the case of := where the first variable is not defined:
    x, inFix := GenerateError()
    This clearly means define 'x', but does it mean define a local variable
    inFix of type error, or use the global inFix(error) function as a hat
    function? ^ removes the ambiguity.

    o2. I've not thought of a way to make this even more generic. At this
    level of definition, a hat function can take only one nil-able parameter.
    Might one think of something more flexible?

    Finally:

    I'd be very interested if more experienced Gopher's tell me I'm doing
    errors all wrong, or that I'm messing up if I'm just passing errors 'up'
    the stack. Please don't hesitate to criticize.

    All the best,
    Craig
    --
    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/groups/opt_out.
  • Volker Dobler at Apr 12, 2013 at 6:37 am
    Your proposal is less than half baked, e.g. error is not a keyword.

    Personally I find the proposed syntax dead ugly.

    On an other level: This type of "error handling" is very unlikely to
    happen. So it might be a waste of time to reason and argue
    about it.

    V.

    Am Freitag, 12. April 2013 00:44:59 UTC+2 schrieb qp...@zabustudio.com:
    A very similar proposal: https://gist.github.com/qpliu/3259111

    The example would be written as:

    error x, return inFix(_) := GenerateError()
    On Friday, April 5, 2013 12:31:11 AM UTC-7, Craig Mason-Jones wrote:

    Hi all,

    I am interested in error-related changes. Frequently I want to check for
    an error, and pass it 'up' if it occurs:

    _, err := someFn()
    if nil!=err { return err }

    I've tidied that up a little to

    if _,err :=someFn(); nil!=err {
    return err
    }

    This almost becomes boilerplate, and causes a lot of tedious repetition
    and duplication. If I change my function return signature, I have to
    duplicate this at each point.

    I would like to have something like this:

    func inFix(err error) (string,error) {
    return "", err
    }

    func GenerateError() (string,error) {
    return "", errors.New("My error")
    }

    func One() (string,error) {
    x, ^inFix := GenerateError()

    return x, nil
    }

    The rules are simple:

    r1. A 'hat' function can take only a single parameter, an interface{} or
    a pointer - anything that can be nil, and MUST return the same values as
    the function in which it is called.

    r2. It is a compilation error to ^ a hat function within a containing
    function that has a different return signature.
    In my example, it would be a compilation error to use ^ inFix
    function in a function returning (int, error)

    r3. If a ^ appears in an assignment position, it must be in the position
    of an interface return value that matches the parameter of the hat function.
    In my example, ^inFix occurs at the position where 'error' is being
    returned. Since inFix takes an error parameter, this matches. Anything else
    would be a compilation error.

    r4. If the parameter returned by the function in the position of the ^
    call is not-nil, the hat function is called, and the return values of the
    hat function are returned by the containing function.

    Implementation notes:

    i1. A crude compilation:

    x, ^h := f(x)

    expands to:

    var err_h error
    // define x if it's not already defined
    if x,err_h = f(x); nil!=err_h {
    return h(err_h)
    }

    Observations:

    o1. The ^ in the syntax is not obviously necessary. However, it does
    prevent ambiguity in the case of := where the first variable is not defined:
    x, inFix := GenerateError()
    This clearly means define 'x', but does it mean define a local
    variable inFix of type error, or use the global inFix(error) function as a
    hat function? ^ removes the ambiguity.

    o2. I've not thought of a way to make this even more generic. At this
    level of definition, a hat function can take only one nil-able parameter.
    Might one think of something more flexible?

    Finally:

    I'd be very interested if more experienced Gopher's tell me I'm doing
    errors all wrong, or that I'm messing up if I'm just passing errors 'up'
    the stack. Please don't hesitate to criticize.

    All the best,
    Craig
    --
    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/groups/opt_out.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedApr 5, '13 at 7:31a
activeApr 12, '13 at 6:37a
posts19
users11
websitegolang.org

People

Translate

site design / logo © 2022 Grokbase