I do not think ternary belongs in Go. As Kevin and disroot noted below, I 
also prefer cleaner syntax even if it is more verbose. Its usage would also 
overlap with with the if statement, so which would you use when? I really 
appreciate the opinionated go fmt (and gofumpt) so we have do not to bother 
with such choices.

Dne nedelja, 16. februar 2020 19.28.00 UTC+1 je oseba lgo...@gmail.com 
napisala:
>
> Great suggestion 
> f := os.Open("filename.ext")
> else err != nil {
>     log.Fatal(err)
> }
>
> is much needed syntax improvement vs the current syntax. 
>
> I would further suggest f ?= os.os.Open("filename.ext") : log.fatal("Cant 
> open filename.ext") 
> But the GO 'custodians' get apoplexy whenever they see any syntax that 
> resembles the C ternary operator.
> Chill-out 'custodians' !! 
> '?' is just a symbol that can be replaced by some another one 
> On Saturday, February 15, 2020 at 6:06:39 PM UTC-5, teknoslo wrote:
>>
>> 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/ec62d749-fb42-4f59-a6a4-e41265855fb1%40googlegroups.com.

Reply via email to