I think for what you want to do you don't need any language extension. You can implement all that within the current language. Look at this example: https://go.dev/play/p/aqjwzknrArH
If you want to catch errors also in nested functions, you can also skip the ErrorChecker type and do it like this: https://go.dev/play/p/3Fk-82zxtUJ If you don't like the named return parameter, you can use a wrapper function: https://go.dev/play/p/C54vQThPUnq I thought about putting something like that in a package on github. But it is such a small code, that I think copy and paste is just fine. :-) Cheers On Sun, Jun 4, 2023 at 6:17 PM Shulhan <m.shul...@gmail.com> wrote: > Dear gophers, > > I have been reading several proposals about error handling couple of > months ago and today a light bulb come upon me, and then I write as much > as I can think. I am not sure if its good or bad idea or even possible > to implement. > > In this post, I will try as concise as possible. > The full and up to date proposal is available at > https://kilabit.info/journal/2023/go2_error_handling/ . > > Any feedback are welcome so I can see if this can move forward. > Thanks in advance. > > == Background > > This proposal is based on "go2draft Error Handling". > > My critics to "go2draft Error Handling" is the missing correlation > between handle and check. > If we see one of the first code in the design, > > ---- > ... > handle err { > return fmt.Errorf("copy %s %s: %v", src, dst, err) > } > > r := check os.Open(src) > ... > ---- > > There is no explicit link between check keyword and how it will trigger > handle err later. > It is also break the contract between the signature of os.Open, that > return an error in the second parameter, and the code that call it. > > This proposal try to make the link between them clear and keep the code > flow explicit and readable. > > The goals is not to reduce number of lines but to minimize repetitive > error handling. > > > == Proposal > > This proposal introduces two new keywords and one new syntax for > statement. > > The two new keywords are “WHEN” and “HANDLE”. > > ---- > When = "when" NonZeroValueStmt HandleCallStmt . > NonZeroValueStmt = ExpressionStmt > ; I am not quite sure how to express non-zero value > ; expression here, so I will describe it below. > > HandleCallStmt = "handle" ( HandleName | "{" SimpleStmt "}" ) . > HandleName = identifier . > ---- > > The HandleCallStmt will be executed if only if the statement in > NonZeroValueStmt returned non-zero value of its type. > For example, given the following variable declarations, > > ---- > var ( > err = errors.New(`error`) > slice = make([]byte, 1) > no1 = 1 > > no2 int > ok bool > ) > ---- > > The result of when evaluation are below, > > ---- > when err // true, non-zero value of type error. > when len(slice) == 0 // true, non-zero value of type bool. > when no1 // true, non-zero value of type int. > when no2 // false, zero value of int. > when ok // false, zero value of bool. > ---- > > The HandleCallStmt can jump to handle by passing handle name or provide > simple statement directly. > If its simple statement, there should be no variable shadowing happen > inside them. > > Example of calling handle by name, > > ---- > ... > when err handle myErrorHandle > > :myErrorHandle: > return err > ---- > > Example of calling handle using simple statement, > > ---- > ... > when err handle { return err } > ---- > > The new syntax for statement is to declare label for handle and its body, > > ---- > HandleStmt = ":" HandleName ":" [SimpleStmt] [ReturnStmt | > HandleCallStmt] . > ---- > > Each of `HandleStmt` MUST be declared at the bottom of function block. > An `HandleStmt` can call other `HandleStmt` as long as the handle is above > the > current handle and it is not itself. > Any statements below `HandleCallStmt` MUST not be executed. > > Unlike goto, each `HandleStmt` is independent on each other, one > `HandleStmt` > end on itself, either by calling `return` or `handle`, or by other > `HandleStmt` and does not fallthrough below it. > > Given the list of handle below, > > ---- > :handle1: > S0 > S1 > :handle2: > handle handle1 > S3 > ---- > > A `handle1` cannot call `handle2` because its below it. > A `handle2` cannot call `handle2`, because its the same handle. > A `handle2` can call `handle1`. > The `handle1` execution stop at statement `S1`, not fallthrough below it. > The `handle2` execution stop at statement "`handle handle1`", any > statements > below it will not be executed. > > > The following function show an example of using this proposed error > handling. > Note that the handlers are defined several times here for showing the > possible cases on how it can be used, the actual handlers probably only > two or > three. > > ---- > func ImportToDatabase(db *sql.DB, file string) (error) { > when len(file) == 0 handle invalidInput > > f, err := os.Open(file) > when err handle fileOpen > // Adding `== nil` is OPTIONAL, the WHEN operation check for NON > zero > // value of returned function or instance. > > data, err := parse(f) > when err handle parseError > > err = f.Close() > // Inline error handle. > when err handle { return fmt.Errorf(`%s: %w`, file, err) } > > tx, err := db.Begin() > when err handle databaseError > > // One can join the statement with when using ';'. > err = doSomething(tx, data); when err handle databaseError > > err = tx.Commit() > when err handle databaseCommitError > > var outofscope string > _ = outofscope > > // The function body stop here if its not expecting RETURN, > otherwise > // explicit RETURN must be declared. > > return nil > > :invalidInput: > // If the function expect RETURN, the compiler will reject and > return > // an error indicating missing return. > > :fileOpen: > // All the instances of variables declared in function body until > this > // handler called is visible, similar to goto. > return fmt.Errorf(`failed to open %s: %w`, file, err) > > :parseError: > errClose := f.Close() > when errClose handle { err = wrapError(err, errClose) } > > // The value of err instance in this scope become value returned by > // wrapError, no shadowing on statement inside inline handle. > return fmt.Errorf(`invalid file data: %s: %w`, file, err) > > :databaseError: > _ = db.Rollback() > // Accessing variable below the scope of handler will not > compilable, > // similar to goto. > fmt.Println(outofscope) > return fmt.Errorf(`database operation failed: %w`, err) > > :databaseCommitError: > // A handle can call another handle as long as its above the > current > // handle. > // Any statements below it will not be executed. > handle databaseError > > RETURN nil // This statement will never be reached. > } > ---- > > That's it. What do you guys think? > > -- > 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. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/20230604231646.78bf1fda%40inspiro.localdomain > . > -- 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. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAFwXxZRmpJhQ5yH6Pgzrum9N92y9WAsT%3DS30oerszqvoxCriOw%40mail.gmail.com.