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.

Reply via email to