This is more of a request for comments and not a complete proposal. I will try to keep this short(ish) and simple.
Note that the `else` I will write about is not to be confused with the usual `else` used with `if`. It is an `else` in a slightly different context. `else` is basically an `if` with a variable initialized to the last return value of a function call from the previous line. Note: "from the previous line" is a simplification. Between the function call and the `else` statement white space and comments are allowed, but nothing else. Another note: "function call" is also a simplification. It can be any statement that returns a value. It could be, for example, the "comma ok" idiom used for testing whether an entry in a map is present. A few simple rules: - Only the last return value is ever taken, not multiple. - If there is no return value, `else` is not possible. - The variable is defined only within the scope of the `else` block. - The name of the variable can be any valid identifier. - Braces are mandatory. Code like this: f, err := os.Open("filename.ext") if err != nil{ log.Fatal(err) } would become: f := os.Open("filename.ext") else err != nil { log.Fatal(err) } The `err` variable in the example above is automatically initialized to the last return value of the function call `os.Open`. The variable could have any valid variable name and is defined only in the `else` block. I like to read the example above with an implicit try before the function call. I.e., try opening filename.ext, else if err is not nil... For this reason, I call this construct else catch. A longer example from Error Handling - Problem Overview <https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling-overview.md>: func CopyFile(src, dst string) error { r, err := os.Open(src) if err != nil { return fmt.Errorf("copy %s %s: %v", src, dst, err) } defer r.Close() w, err := os.Create(dst) if err != nil { return fmt.Errorf("copy %s %s: %v", src, dst, err) } if _, err := io.Copy(w, r); err != nil { w.Close() os.Remove(dst) return fmt.Errorf("copy %s %s: %v", src, dst, err) } if err := w.Close(); err != nil { os.Remove(dst) return fmt.Errorf("copy %s %s: %v", src, dst, err) } } would become: func CopyFile(src, dst string) error { r := os.Open(src) else err != nil { return fmt.Errorf("copy %s %s: %v", src, dst, err) } defer r.Close() w := os.Create(dst) else err != nil { return fmt.Errorf("copy %s %s: %v", src, dst, err) } io.Copy(w, r) else err != nil { w.Close() os.Remove(dst) return fmt.Errorf("copy %s %s: %v", src, dst, err) } w.Close() else err != nil { os.Remove(dst) return fmt.Errorf("copy %s %s: %v", src, dst, err) } } By using the else catch we declutter the function calls, more specifically assignments, thus improving readability. Error handling stays familiar, explicit, and simple. And since the variable in else catch is defined only in the `else` block, we do not need to re-assign the `err` variable again and again, making the code more obvious and unambiguous. I would like to note that the else catch example is longer, but I think length should not be equated with readability. Else catch is not meant to replace the `if err != nil` idiom, but rather complement it. It would allow for a clear separation of return values we "care" about and error handling. Or put another way, it would allow for a clear separation of a happy and an unhappy path of execution. What about the error handling blocks? We should not try to stash away error handling. Most gophers are already used to skipping error handling blocks while reading Go. Using else catch would make that even easier. IDEs could collapse else catch blocks on command. Else catch is orthogonal and can be used for anything, not just error handling. We just have to remember that it always takes the last (and no more) return value of a function call from the previous line. For example, we can test for existence of a key: m := map[string]int{ "route": 66, } i := m["route"] else !ok { i = -1 } Now, there is a slight problem with current else catch. If it is basically an `if`, what happens if we use multiple variables in the condition? Which variable came from the last return value? firstTimeBonus := true apples := map[string]int{ "bob": 7, ... } apples["john"] else !ok && firstTimeBonus { apples["john"] = 3 } In this case we can probably figure out that variable `ok` is the last return value of `apples["john"]`. But it is not explicitly stated that `ok` came from the last return value. What if `ok` variable was already declared and we unknowingly shadowed it? And how would the compiler know to which variable to assign the last return value? This could be solved by allowing only one variable in the `else` condition. Then the example above would be invalid, which it should be, because the if statement would be more appropriate. Also if there was only one variable allowed in the condition, there would be no confusion as to where the variable came from. I believe something like else catch could be beneficial to Go, mainly improving readability. But there are a few problems to consider. `else` would have different semantics depending on the context. This should not be a problem for people, but it could be for the compiler and other tools. Go would have a new, not widely known construct. How much would it impact the learning curve of the language? This is just an idea and I am interested in what you think. - Would else catch be useful to you? - Could it be simpler? Maybe a keyword or a built-in that could take the last return value and we could use if with a short statement instead of `else`. - Do you see a problem with it? Does it feel too much like magic? - Is it trying to solve a problem you even have? - Do the pros outweigh the cons? -- 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/06c41b88-5520-45b2-aad8-e3e74a2b1a86%40googlegroups.com.