In the C world one can do things like

int foo(char *infile, *outfile) {
  int n = -1, in, out = -1;
  ...
  if ((in = open(infile, O_RDONLY)) < 0 ||
      (out = creat(outfile, 0666)) < 0 ||
      (n = read(infile, buf, sizeof buf)) <= 0)
    goto err;
  ...
  return 0;
err:
  close(in);
  if (out >= 0) close(out);
  if (n == 0) return 0;
  return -errno; // or just return -1 since the caller can check errno
}

Here we are not ignoring any errors but we factored out error handling. This is 
possible because an assignment can be used in an expression and since errno is 
only meaningful in case of a syscall fails, making it a global allowed this 
style of coding. Chaining expressions is very convenient in general.

To provide a similar facility in Go, one or two things have to be allowed.

1: provide  GnuC like facility of using a statement in an expression -- note 
that Go already does this for if-stmts but it can be generalized. Like in GnuC, 
the syntax can be
  
  expr: ... | '(' stmt ';' expr ')'

This is clunky and I didn't even like it in GnuC (where you need an extra pair 
of braces!) but suggesting it as it is very similar to what is done with if 
today.

The other option is to allow an assignment in an expression. Initial thought: 
If the assignment *fails*, the expression evaluates to *false*. Thus....

  if !(x, err := foo()) || !(y, err := bar(x)) { goto cleanupAndReturn }
  ...
  cleanupAndReturn: ...

Then !(x, err := foo()) says foo didn't return an error.

This is also a bit clunky. However, when something returns an error, the 
expression fails seems almost intuititve and easy to explain. On the other 
hand, it makes errors special as opposed to just another value.

Yet another option is to use union or sum types , using "|" to separate the 
alternative types. And an assignment has the same type as the type of the RHS 
expression. Then...

func foo()(int|error) { // notice TYPE|TYPE syntax
...
  if isError(x = foo()) || isError(y = bar(x)) { goto cleanupAndReturn }

Here isError() is a predefined function that returns true if the given 
parameter is of error type.

I like this because a union type, assignment as expression, and isError() are 
all generally useful. /usr/local/go/src has over 9000 assignments of kind 
'value, err = function(...)' or  'value, err := function(...)'. A sumtype would 
clean this up quite a bit.  [I did do a write up of sumtypes feature but 
haven't decided if it is worth proposing]

Anyway, if you want to error handling a bit more convenient, there are less 
magical ways of doing so than "watch". And I do believe that any new feature 
should work well with existing features and be generally useful.


> On Sep 5, 2017, at 5:06 PM, Michael Jones <michael.jo...@gmail.com> wrote:
> 
> ...Go is not perfect. But there is subtle magic in this error business. 
> Developers should look closely at errors. Imagine handling them as if you 
> were a doctor and the error was a child's symptom or a test result. Choices:
> 
> 1. Ask them how they feel, ignore it. Do a test, ignore it. Effectively you 
> are saying "you are well" even if they're not breathing.
> 
> x, _ := ResultAndErr(a,b,c)
> 
> 2. Ask them if they feel great and if all test results are normal. If not, 
> send them home.
> 
> x, err := ResultAndErr(a,b,c)
> if err != nil {
>     return
> }
> 
> 3. Ask them if they feel great and if all test results are normal. If not, 
> send them to a hospital where they can talk to a real doctor.
> 
> x, err := ResultAndErr(a,b,c)
> if err != nil {
>     log.Error(err, a,b,c) // or return err
> }
> 
> 4. Ask them how the feel and if not good, do tests. Then TREAT THEIR 
> CONDITION.
> 
> x, err := ResultAndErr(a,b,c)
> if err ==FirstPossibleProblem {
>     // HANDLE error1 // reset power, wait, etc.
> } else if err ==SecondPossibleProblem {
>     // HANDLE error2 // lower temperature, send text, start siren, ...
> }
> :
> 
> These four kinds of doctors closely parallel four kinds of programmers.
> 
> On Tue, Sep 5, 2017 at 3:47 PM, Dorival Pedroso <pedr...@cpmech.com 
> <mailto:pedr...@cpmech.com>> wrote:
> Since defer "is the last thing to happen", I think this code is all right:
> 
> func something() (x int, err error) {
>     watch err != nil {
>         return
>     }
>     res, err = acquireResource()
>     defer func() {
>         if err == nil {
>             err = res.Close()
>         }
>     }()
>     err = abc1()
>     err = abc2()
>     err = abc3()
>     err = abc4()
> }
> 
> Also, I'd like to make a general comment (not specific to this case). The 
> watch command doesn't seem (to me) a bad idea. And it could be used for other 
> purposes too (e.g. as the "watch expression" feature in debuggers or for 
> logging---yeah we don't really need it...).
> 
> For instance:
> 
> func calculations() {
>     x := 100.0
>     watch x < 0 {
>         fmt.Printf("hey, x is negative (%d)\n", x)
>         fmt.Printf("...no worries...\n")
>     }
>     x = compute(1,2,3)
>     x = compute(3,2,1)
>     x = compute(1,3,2)
> }
> 
> It's of course NOT needed at all. Above, we can do the same thing in a 
> different (probably better) way.
> 
> In fact, Go is perfect the way it is and doesn't need any change at all!
> 
> Also, the mechanics (implementation) of watch may be "impossible", "too 
> hard", "confusing" or at least "inefficient"---I don't know (no idea...).
> 
> With an opportunity to work on the Go language implementation, I would rather 
> spend some time tweaking things to be faster...
> 
> Cheers!
> Dorival
> 
> On Wednesday, September 6, 2017 at 1:11:36 AM UTC+10, eko...@gmail.com 
> <mailto:eko...@gmail.com> wrote:
> I've been doing something like this for long chains where "handle error" is 
> the same:
> 
> func something() (x int, err error) {
>     defer func() {
>         if err != nil {
>             // handle error
>         }
>     }()
>     res, err = acquireResource()
>     if err == nil {
>         defer func() {
>             if e := res.Close(); err == nil {
>                 err = e
>             }
>         }()
>         err = abc1()
>     }
>     if err == nil {
>         err = abc2()
>     }
>     if err == nil {
>         err = abc3()
>     }
>     if err == nil {
>         err = abc4()
>     }
> }
> 
> How would watch interact with defer?
> 
> On Monday, September 4, 2017 at 8:27:20 PM UTC+2, marti...@programmfabrik.de 
> <> wrote:
> Hi guys,
> 
> at first I though I really like the idea of how Go deals with error 
> management and handling, but the more Go code I look at or try to program, 
> the more I get scared about checking errors every second line in every given 
> block of code.
> 
> Take a look at this example here from "Build Web Application with Golang":
> 
> // insert
> stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) 
> values(?,?,?)")
> if err != nil {
>   // handle error
> }
> res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
> if err != nil {
>   // handle error
> }
> id, err := res.LastInsertId()
> if err != nil {
>   // handle error
> }
> fmt.Println(id)
> // update
> stmt, err = db.Prepare("update userinfo set username=? where uid=?")
> if err != nil {
>   // handle error
> }
> res, err = stmt.Exec("astaxieupdate", id)
> if err != nil {
>   // handle error
> }
> affect, err := res.RowsAffected()
> if err != nil {
>   // handle error
> }
> 
> Seriously? And yes, I have read https://blog.golang.org/errors-are-values. 
> <https://blog.golang.org/errors-are-values.>..
> 
> The best case reduction I found is:
> 
> ...
> res, err = stmt.Exec("astaxieupdate", id)
> checkError(err)
> ...
> 
> Still, I need this after each line of calling a function which may return an 
> error.
> 
> I bet this is not pleasant to do in larger code bases and it also takes away 
> focus from what is actually happening.
> 
> 50-80% of all lines of code in my example deal with error handling?
> 
> This is not good. Seriously.
> 
> And don't get me wrong, there is a lot of things I really like, love and 
> adore about Go, but catching errors needs an improved syntax!
> 
> And I am not proposing try...catch here. 
> 
> How about introducing a new piece of syntax 
> 
> "watch if  .... " 
> 
> which tells the compiler to watch out for changes in a given SimpleStmt
> 
> The same code as above would look like this:
> 
> var err Error
> 
> watch if err != nil {
>   // handle error(s)
> }
> 
> // insert
> stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) 
> values(?,?,?)")
> res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
> id, err := res.LastInsertId()
> fmt.Println(id)
> 
> // update
> stmt, err = db.Prepare("update userinfo set username=? where uid=?")
> res, err = stmt.Exec("astaxieupdate", id)
> affect, err := res.RowsAffected()
> 
> The "watch if" would be executed after each assignment of any of the 
> variables used in SimpleStmt of the statement.
> Multiple "watch if" would be executed in order or appearance
> The "watch if" could be used like "defer..." inside functions
> The "watch if" would work in its full scope of the watched variables
> I am not a language expert, so may be there is a saner way of expression what 
> I want to achieve.
> 
> But bottom line is, there should by an easier to read and write way to deal 
> with errors in Go.
> 
> 
> Martin
> 
> 
> 
> 
> 
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to golang-nuts+unsubscr...@googlegroups.com 
> <mailto:golang-nuts+unsubscr...@googlegroups.com>.
> For more options, visit https://groups.google.com/d/optout 
> <https://groups.google.com/d/optout>.
> 
> 
> 
> -- 
> Michael T. Jones
> michael.jo...@gmail.com <mailto:michael.jo...@gmail.com>
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to golang-nuts+unsubscr...@googlegroups.com 
> <mailto:golang-nuts+unsubscr...@googlegroups.com>.
> For more options, visit https://groups.google.com/d/optout 
> <https://groups.google.com/d/optout>.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to