Re: [go-nuts] suggestion: simple mechanism for error propagation

6 views
Skip to first unread message
Message has been deleted

Bob Cunningham

unread,
Nov 22, 2009, 1:29:41 AM11/22/09
to inspector_jouve, golang-nuts
On 11/21/2009 09:32 PM, inspector_jouve wrote:
> Since the issue of exceptions is still under discussion, here's a
> simple idea.
> Right now, if function returns an error, we can ignore it by using the
> idiom:
> x,_=foo(...)
> In this particular context, token "_" , basically, stands for "ignore
> error"
> The idea is to introduce another token - tentatively, "^", which
> stands for "return error, if non-null, to the caller", so that
> invocation will look like:
> x,^=foo()
> Semantics of this operation can be defined in rather straightforward
> manner.

This is really only syntactic sugar. Your example above is equivalent to:
x, ok := foo();
if !ok { return }
Which is already a well-established Go idiom. (Check it out using Google code search.)

Isn't this way too small of a reason to mandate a change to the Go language spec, documents and compilers?

Unless, of course, you are volunteering to do all these updates! In which case I'd say go ahead, create and submit the required patches, participate in the code reviews, and work to get them approved and incorporated.

> Consider 2 cases:
> - If current function declares os.Error in its return list, "^" causes
> function to return immediately with the error received.
> [ Naturally, all deferred calls defined by the function are executed -
> as with any return ]
>
> - If current function doesn't declare os.Error it its return list,
> system simulates return not only in this function, but also in the
> caller, in the caller of caller, etc, until it reaches someone who
> does declare os.Error in its return list, where we are back to case 1.
> [ Again, all deferred calls are executed while simulating returns ]
>
> This mechanism has one drawback: suppose we call function bar(), which
> does not declare any errors (in other words, it's a function of second
> kind), but we WANT to handle errors occurring during the execution of
> bar(). In the language, we introduce special form of function
> invocation:
> x,err:= catch bar();
>
> This operator "catch" is equivalent to defining the modification of
> bar which DOES declare error:
> func bar1() (x int, err os.Error) {
> /* same body as bar */
> }
> and calling it instead of bar.

"catch" is problematic, since when the compiler encounters "catch", it must inspect the binary of the called function to determine if that function has any instances of "_ :=". I suspect it is impossible, since any decent optimizer will eliminate such evidence.

If we insist that the ELF contain references to all instances of ",_:=", then we may limit the power of the optimizer. But the real problem is that the compiler must then MODIFY THE BINARY of the called function to "patch in" the effect of the ",^:=" syntax!

What if "catch" is used to interrupt the flow of a program, where forcing a return could cause the application to abort? In such situations, "catch" could CREATE bugs in good code!

Why stop at one level? What about functions called by the called function? Do you drill "all the way down"?

> Another new operator is (predictably) "throw expr" where expr is an
> expression of type os.Error
> Statement "throw x" has obvious semantics, equivalent to ^:=x
> (so, strictly speaking, we don't need this operator, can just use
> ^:=x, but "throw" is more readable; note that in case when function
> does declare os.Error in return list, operator is equivalent to normal
> return with error)

Throw, aside from being redundant, also has syntactical issues that could make it harder to implement than "val, ^ :=".

> It seems that the whole thing is functionally equivalent to
> traditional exception mechanism.
> Even more importantly, it doesn't seem to make too great damage to
> the language (at least I hope so).

Some rules/clarifications to consider adding:

1. A function may not use the "val, ^ :=" syntax *unless* it has an os.Error defined as part of its own return.

2. Provide a compiler flag that converts the behavior of all "val, _ :=" to be that of "val, ^:=". This may make "catch" irrelevant.


We must be careful not to make the "val, _ :=" syntax illegal or even undesirable, and we must not make the "val, ok :=" syntax mandatory.


-BobC
Message has been deleted
Message has been deleted

Brian Slesinsky

unread,
Nov 22, 2009, 2:56:05 PM11/22/09
to golang-nuts

On Nov 21, 10:29 pm, Bob Cunningham <FlyM...@gmail.com> wrote:
> This is really only syntactic sugar.  Your example above is equivalent to:
>    x, ok := foo();
>    if !ok { return }
> Which is already a well-established Go idiom.  (Check it out using Google code search.)

I agree that non-local return is complicated and probably not
necessary, so long as local returns have concise syntax. However, I
disagree about the "well-established Go idiom". The most well-
established Go idiom is to ignore the error. This happens in most
places fmt is used - in the tutorial, in the standard libraries, even
in "hello world". This is something Go has picked up from C that could
use some improvement.

Proper error handling in Go currently looks something like this:

if _, err := fmt.Print("something"); err != nil { return err; }

However, this is unnecessary boilerplate that doubles the number of
characters to do something that's utterly routine. It's sufficiently
tedious that it's the exception rather than the rule.

I'd like to see a shorthand that looks something like this:

fmt.Print("something")^;

If you can handle an error in the usual way by writing one or two
extra characters, it's much more likely to be done routinely. In most
languages with exceptions (and when using RuntimeException in Java),
you need zero extra characters to propagate errors, but it's difficult
to see where early exits can happen, so I think that's going a bit too
far. Marking the places where an early return can happen with one
extra character seems about right.

- Brian
Message has been deleted
Message has been deleted

Peter Froehlich

unread,
Nov 24, 2009, 12:41:52 AM11/24/09
to inspector_jouve, golang-nuts
Hi all,

On Tue, Nov 24, 2009 at 12:21 AM, inspector_jouve <kaush...@gmail.com> wrote:
> Still, an argument can be made in favour of syntax ^:=fmt.Print().
> When we invoke  fmt.Print, there are 3 alternatives:
> - ignore the error
> - handle the error
> - return error to caller
>
> We need consistent syntax for these alternatives, which is:
> _:=fmt.Print(...)
> err:=fmt.Print(...)
> ^:=fmt.Print();
>
> What's wrong with idiom
> if  err := fmt.Print("something"); err != nil { return err; }
>
> is not that it requires boilerplate, but rather that its meaning
> (invocation of fmt.Print) gets obscured by this boilerplate; there
> shouldn't be any "if" here. The form ^:=fmt.Print(...) is much more
> readable, not just faster to type.

I am surprised to find myself in relative agreement on the surface of
things. I've been writing lots of lines that amounted to this "return
the error (if any)" mantra, so a shorter way of doing this would be
welcome.

I *do* have a problem with hiding a pretty major control-flow decision
in a tiny piece of syntax that doesn't even remotely look like
control-flow. But I *could* get over that. :-D

But "return err;" may not be workable if you return multiple results.
A better translation might be

if locallyUniqueName := fmt.Print("something"); locallyUniqueName !=
nil { realErrorResultName = locallyUniqueName; return; }

which opens a whole new can of worms since now you're making even more
implicit. And what if you are inside a function that returns *two*
os.Error values? Now you have to say something like

realErrorResultName^ := fmt.Print("something")

or some other variation that makes the name explicit. :-/

So while I could be happy with the ^ syntax, the language purist in me
is queasy. And I think the proposal has little chance of gaining a
following @ golang.org because of trouble like that.

Cheers,
Peter
--
Peter H. Froehlich <http://www.cs.jhu.edu/~phf/>
Senior Lecturer | Director, Johns Hopkins Gaming Lab
Message has been deleted

Peter Froehlich

unread,
Nov 24, 2009, 1:14:39 AM11/24/09
to Alex Kaushansky, golang-nuts
On Tue, Nov 24, 2009 at 12:52 AM, Alex Kaushansky <kaush...@gmail.com> wrote:
> No, there's no problem with returning multpile values.
> Note that "^" is pseudovariable, like "_".
> So you can easily assign:
> x,y,z,^:=foo(...)

Not the function you *call* returning multiple values, the function
you are *calling* from returning multiple values, including possibly
multiple os.Error values.
Reply all
Reply to author
Forward
Message has been deleted
0 new messages