Idea extending xerror.As

瀏覽次數:220 次
跳到第一則未讀訊息

Haddock

未讀,
2021年9月19日 清晨5:33:002021/9/19
收件者:golang-nuts

I like the way error handling is done in the xerror package. Things become more concise, but remain very easy to read and understand as in plain Go errorhandling.

Here is the example of how to use xerror.As:

_, err := os.Open("non-existing")
    if err != nil {
        var pathError *os.PathError
        if xerrors.As(err, &pathError) {
            fmt.Println("Failed at path:", pathError.Path)
        }
    }

My idea is to make this even shorter like this:

_, err := os.Open("non-existing")
myerrors.As(err, os.PathError) {
     pathError -> fmt.Println("Failed at path:", pathError.Path)
}

Think something like that has so far not been suggested. That's why I thought it is justified to drop comment.

myerrors.As would also do the check if err is nil. The code in my sample is not valid Go code, I know. It is only pseudo code to show the idea.

Brian Candler

未讀,
2021年9月19日 清晨7:24:132021/9/19
收件者:golang-nuts
I notice at least three things from your proposed syntax:

1. Passing a *type* as an argument ("os.PathError"), rather than a *value of a type*
2. A control structure.  This might involve passing a block of code as a hidden argument (like blocks in Ruby), or some sort of macro expansion.
3. The syntax "pathError -> fmt.Println"; I can't see what this is supposed to do.

I don't know which of these (or all of them) is critical to your idea, but I think they need to be identified separately.

Brian Candler

未讀,
2021年9月19日 清晨7:38:492021/9/19
收件者:golang-nuts
Also, I may be missing the point, but does the following do what you want?

_, err := os.Open("non-existing")
if pe := (&os.PathError{}); errors.As(err, &pe) {
fmt.Println("Failed at path:", pe.Path)
}

Haddock

未讀,
2021年9月19日 下午1:36:432021/9/19
收件者:golang-nuts
Brian Candler schrieb am Sonntag, 19. September 2021 um 13:38:49 UTC+2:
Also, I may be missing the point, but does the following do what you want?

_, err := os.Open("non-existing")
if pe := (&os.PathError{}); errors.As(err, &pe) {
fmt.Println("Failed at path:", pe.Path)
}

 Oh, this is cool. Thanks, Brian! When Go gets generics you can then write something like this (pseudo code):

_, err := os.Open("non-existing")
myerrors.As(err, os.PathError, func(pathError)  {
     fmt.Println("Failed at path:", pathError.Path)
})

And this is absolutely good enough for my purposes ;-)

Brian Candler

未讀,
2021年9月19日 下午2:46:452021/9/19
收件者:golang-nuts

In fact, it can infer the type parameter:

I'm not sure how useful that is though, especially because "return" inside that anonymous function will return from that anonymous function, not the outer function.

David Finkel

未讀,
2021年9月19日 下午4:03:232021/9/19
收件者:Haddock、golang-nuts
You might be interested in the original draft proposal for errors.As:
https://go.googlesource.com/proposal/+/master/design/go2draft-error-inspection.md#the-is-and-as-functions

In particular, it originally specified that errors.As would take a type-parameter. (the version of generics that was proposed concurrently with that proposal was not accepted so they had to go with the current (clunkier) interface).

--
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/629e6763-36a9-4d7d-991c-fd71dd384d0en%40googlegroups.com.

David Finkel

未讀,
2021年9月19日 下午4:16:112021/9/19
收件者:Haddock、golang-nuts
On Sun, Sep 19, 2021 at 4:02 PM David Finkel <david....@gmail.com> wrote:
You might be interested in the original draft proposal for errors.As:
https://go.googlesource.com/proposal/+/master/design/go2draft-error-inspection.md#the-is-and-as-functions

In particular, it originally specified that errors.As would take a type-parameter. (the version of generics that was proposed concurrently with that proposal was not accepted so they had to go with the current (clunkier) interface).

Hmm, actually, the code in that proposal for the generic version of errors.As works almost unchanged: https://go2goplay.golang.org/p/ddPDlk00Cbl (I just had to change the type-parameter syntax)

roger peppe

未讀,
2021年9月19日 下午5:20:162021/9/19
收件者:David Finkel、Haddock、golang-nuts
In some ways, the existing API is arguably more ergonomic than the originally proposed generic version, as it's possible to use `errors.As` in a switch statement (eg to test several possible types of error) which isn't possible with the multi-return `As` variant.

A minor variant of the existing API could be:

```
func As[E error](err error, asErr *E) bool
```
which makes the API a little clearer without changing the usage. Sadly we can't make that change without breaking compatibility.


David Finkel

未讀,
2021年9月19日 下午5:43:362021/9/19
收件者:roger peppe、Haddock、golang-nuts
On Sun, Sep 19, 2021 at 5:19 PM roger peppe <rogp...@gmail.com> wrote:
In some ways, the existing API is arguably more ergonomic than the originally proposed generic version, as it's possible to use `errors.As` in a switch statement (eg to test several possible types of error) which isn't possible with the multi-return `As` variant.

Hmm, that's a good point.
However, the main reason I like the two-return-value version more is that you can use it like a normal type-assertion in an if-statement's init section.


A minor variant of the existing API could be:

```
func As[E error](err error, asErr *E) bool
```
which makes the API a little clearer without changing the usage. Sadly we can't make that change without breaking compatibility.

Unfortunately, in order to use this proposed version, you still need to pre-declare the variables for each type before the switch/case.
I've honestly found it more ergonomic to use a if/else if/ block rather than a switch/case because it lets me contain the scope of these variables anyway.

I suppose a simple wrapper that can be used with type-assertions inside a switch/case block would be:
```
func AsBool[E error](err error, asErr error) bool {
    ae, ok := As[E](err)
    if ok {
         asErr = ae
    }
    return ok
}
```
(this would definitely need a better name)

Then you'd be able to almost treat your switch/case like a type-switch without needing to pre-declare a variable for every case.

```
var asErr error
switch {
   case errors.AsBool[*os.PathError](err, &asErr):
       fmt.Printf("Path Error! ae: %v", asErr.(*os.PathError))
   case errors.AsBool[syscall.Errno](err, &asErr):
       fmt.Printf("ae: %d", asErr.(syscall.Errno))
}
```

However, I think it would be nicer to use the (originally proposed) two-return errors.As with if/else if.

```
if pe, ok := errors.As[*os.PathError](err); ok {
    fmt.Printf("Path Error: %v", pe)
} else if en, ok := errors.As[syscall.Errno](err); ok {
    fmt.Printf("errno %[1]d: %[1]s", en)
}
```

Since it looks like the dev.typeparams branch has been merged into master, I was just thinking about how we'd add the two-return-value/generic version of As to the errors package (for go 1.18).
Given that the original proposal's code works pretty much as-is, I think the biggest barrier would be a good name. (given that As is already taken)

Andrey T.

未讀,
2021年9月20日 晚上10:51:522021/9/20
收件者:golang-nuts

... or, to put a crazy idea out there, we need to ask for extension of switch statement to support (v, err) tuples for a case argument...

David Finkel

未讀,
2021年9月21日 上午10:23:032021/9/21
收件者:Andrey T.、golang-nuts
On Mon, Sep 20, 2021 at 10:52 PM Andrey T. <xnow4f...@sneakemail.com> wrote:

... or, to put a crazy idea out there, we need to ask for extension of switch statement to support (v, err) tuples for a case argument...

That's not a particularly crazy idea. It's just one that's unlikely to make it through with one use-case like this since the bar for language changes is so much higher than standard library changes. (for good reason)

Haddock

未讀,
2021年9月23日 下午4:24:322021/9/23
收件者:golang-nuts
Brian Candler schrieb am Sonntag, 19. September 2021 um 20:46:45 UTC+2:

I'm not sure how useful that is though, especially because "return" inside that anonymous function will return from that anonymous function, not the outer function.

 Yes, this is a problem. Then after calling myerror.As in the next line you still to check whether err was not nil and if it was handled, and if so, do a return.

> Unfortunately, in order to use this proposed version, you still need to pre-declare the variables for each type

Yes, that is tedious.

To solve the problem that a return from the error block would only jump out of the As function, but not out of the surrounding function, either some special new language construct needs to be added for handling errors (any kind of function would always bear the problem that a return from the function is a local return and not a non-local as a return from an if clause) or things just stay with:

_, err := os.Open("non-existing")

if pe, ok := errors.As[*os.PathError](err); ok {
    fmt.Printf("Path Error: %v", pe)
} else if en, ok := errors.As[syscall.Errno](err); ok {
    fmt.Printf("errno %[1]d: %[1]s", en)
}

Not too bad, but still a bit painful considering how often you have to write this kind of code for all the errors that need to be handled


回覆所有人
回覆作者
轉寄
0 則新訊息