Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Go 2: return error on assignment to _ #65345

Closed
1 of 4 tasks
jellyterra opened this issue Jan 29, 2024 · 1 comment
Closed
1 of 4 tasks

proposal: Go 2: return error on assignment to _ #65345

jellyterra opened this issue Jan 29, 2024 · 1 comment
Labels
error-handling Language & library change proposals that are about error handling. LanguageChange Suggested changes to the Go language Proposal v2 An incompatible library change
Milestone

Comments

@jellyterra
Copy link

Go Programming Experience

Experienced

Other Languages Experience

Go, C, Scala, Kotlin

Related Idea

  • Has this idea, or one like it, been proposed before?
  • Does this affect error handling?
  • Is this about generics?
  • Is this change backward compatible? Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit

Has this idea, or one like it, been proposed before?

No

To be honest, I'm more hopeful about the tools the community develops for the proposal than about it being adopted as part of a new version of Go.

If the Go team do not appreciate the proposal, I hope somebody (including myself) in Go community can implement a tool for this proposal to avoid classic error handling in Go.

My implementation is not finished yet when I wrote this proposal.

Because if the needs of downstream developers (Go users) are not met, then we cannot count on upstream (Go team and libraries developers).

Does this affect error handling?

Yes.

Suppose there is a builtin or intrinsic function x accepts error only, tentatively designated as panic here.

Let the intrinsic function x be replaced to a ReturnStmt with zero value inferred by the compiler and the error passed to the caller in ReturnStmt.

Example:

v, err := IScream()
if err != nil {
    return 0, err
}

to be:

v, _ := IScream()
// Let compiler infer the zero values and pass the error.

And, it is not necessary to stick to this form, you can also provide a reserved identifier for such situation, which is more friendly to external tools based on Go AST.

v, __reserved_err_or_any_else := IScream()
// It is more friendly to external tools based on AST,
// because they do not need to check the function type anymore,
// the programmer already told to the tool by the reserved identifier.

// Let compiler infer the zero values and pass the error.

Why?

Modifying Golang is breaking existing Go tools like toolkit, IDEs and attached components.

So the proposal try to support error handle in another implicit way without breaking them.

Until Go 1.21.6 version, there are only two paths of function exit in Go:

  • return
  • panic

If you choose panic as the replacement of classic if err != nil, it will not break the check built in existing IDEs.

  • When the users use the feature provided by the proposal, they can avoid writing if err != nil when they really do not need to process the error in current function.
  • Else they get a panic on error, informally reported as an unhandled error.

Is this about generics?

No

Proposal

Suppose there is a builtin function x accepts error only, tentatively designated as panic here.

  • For any function whose result ends with error type:
    New semantic to the x function: when the panic(v) parameter v is error, replace it with ReturnStmt, and let the compiler infer the zero value.
  • In AssignStmt, when the last receive variable is _ or reserved identifier:
    Add the BranchStmt check the implicit error if err != nil { return 0, err } after it.

Language Spec Changes

  • Add, or change a builtin or intrinsic function, which passes non-nil error to the caller.
  • Assign a new semantic to CallExpr or CallExpr in AssignStmt

Informal Change

Classic:

func IScream() (int, error) {
    ...
    if err != nil {
        return 0, err
    }
    ...
}

func ICallYourName() (int, error) {
    v, err := IScream()
    switch err.(type) {
    case nil:
    case XyzError:
        // Handle error.
    default:
        return 0, err
    }
    ...
}

Panic/recover:

func IScream() int {
    ...
    if err != nil {
        panic(err)
    }
    ...
}

func ICallYourName() int {
    defer func() {
        switch v := recover().(type) {
        case nil:
            return
        case XyzError:
            // Handle error.
        default:
            panic(v)
        }
    }()
    v := IScream()
    ...
}

This proposal:

Suppose there is a builtin function x accepts error only, tentatively designated as panic here.

func IScream() (int, error) {
    ...
    if err != nil {
        panic(err) // return 0, err
    }
    ...
}

func ICallYourName() (int, error) {

    // When I want to pass the error to the caller:
    v, _ := IScream() // implicit
//  if _err_UUID != nil {
//      return 0, _err_UUID
//  }

    // When I want to handle this error:
    v, err := IScream()
    if err != nil {
        // Process this error.
        ...
    }

    return v, nil
}

Is this change backward compatible?

YES or NO.

No breaking change, but not backward compatible in such case:

v, _ := IScream() // Oh no, you ignored the error!

But it can be avoided by reversed identifier

v, _ := IScream() // It won't be affected.

v, _err := IScream() // Reasonably affected.

And it is also completely compatible with existing modern Go tools, IDEs when the x is panic .

So it DEPENDS ON YOUR CHOICE AND IMPLEMENTATION too.

Orthogonality: How does this change interact or overlap with existing features?

It is more simple than

  • new operator for error handling [Issue 64493]
  • throw, raise and so on
  • wrap, unwrap error handling helper, intrinsic function

Would this change make Go easier or harder to learn, and why?

What good it brings?

  • Simply and clear code.
  • Less code on if err != nil

What complexity it brings? Users have to learn more about the new semantic of

  • the builtin or intrinsic function x
  • the last ident in AssignStmt.Lhs.(IdentList).List when the callee's last result is error

Depends on choice and implementation.

Cost Description

This proposal strives to avoid changes in the language and language tool ecosystem due to the proposal.

Changes to Go ToolChain

gopls

Performance Costs

Compile cost depends on implementation. Runtime overhead is much smaller compared with panic/recover in Go std libraries, ha-ha-ha.

Prototype

Suppose there is a builtin function x accepts error only, tentatively designated as panic here.

Here is a reference implementation below. NOTE: pseudocode inside []

// NOT necessary to STICK to this implementation, it is just one easy way!

func MODIFY_LHS(...) syntax.Stmt {
    // Rewrite the left list expression in the AssignStmt,
    // so we can replace the _ or reserved identifier to a generated identifier.
    ...
}

if [the last result of this func is error] {
    var changes []syntax.Stmt

    for _, stmt := range blockExpr.List {
        switch stmt := stmt.(type) {
        case AssignStmt:
            if stmt.Rhs.(syntax.CallExpr).Callee.Result[last].Type.Name == "error" {
                changes := append(changes, MODIFY_LHS(stmt))
                changes := append(change, BranchStmt{
                    Cond: [ if _err != nil ],
                    Then: [ return (nil values,) _err ],
                })
            }
        case CallStmt:
            if stmt.Callee.Name == "panic" {
                changes := append(change, BranchStmt{
                    Cond: [if _err != nil],
                    Then: [ return (nil values,) _err ],
                })
            }
        default:
            changes := append(changes, stmt)
        }
    }
}

My implementation is not finished yet when I wrote this proposal.

I'm not familiar with the packages under cmd/compile/internal, I am discovering it while implementing the proposal.

@jellyterra jellyterra added LanguageChange Suggested changes to the Go language Proposal v2 An incompatible library change labels Jan 29, 2024
@gopherbot gopherbot added this to the Proposal milestone Jan 29, 2024
@seankhliao seankhliao added CherryPickCandidate Used during the release process for point releases error-handling Language & library change proposals that are about error handling. and removed CherryPickCandidate Used during the release process for point releases labels Jan 29, 2024
@seankhliao
Copy link
Member

See previous proposals such as #22122 #50207 #52415 #42214 #32884 #32601 #56355 #33150 #35644

I don't see anything new that this proposal proposes, and it is a major breaking change to redefine _ from ignore into returning an error.

@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Jan 29, 2024
@seankhliao seankhliao changed the title proposal: Go 2: Implicit ERROR HANDLE with NO syntax change. proposal: Go 2: return error on assignment to _ Jan 29, 2024
@gabyhelp gabyhelp mentioned this issue Dec 23, 2024
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
error-handling Language & library change proposals that are about error handling. LanguageChange Suggested changes to the Go language Proposal v2 An incompatible library change
Projects
None yet
Development

No branches or pull requests

3 participants