Thanks for the pointer Sven!

I think I was once told not to use panic/defer/recover to implement generic 
exceptions in Go, and never reconsidered it. I think I'm taking up on your 
suggestion for my library.


On Wednesday, June 28, 2023 at 7:30:41 PM UTC+2 Sven Anderson wrote:

> 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.sh...@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...@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/a9885370-a81c-4598-8871-a5a3a831d329n%40googlegroups.com.

Reply via email to