loading...
Cover image for My Favorite Go 2 Proposals

My Favorite Go 2 Proposals

deanveloper profile image Dean Bassett ・3 min read

As some gophers may have heard, Go 2 may be coming soon! It's still in the planning phase though, and the Go team is still working on what features should and shouldn't be in the language.

One of my favorite things about Go is its lack of syntactic sugar, it really feels like I'm problem solving, rather than writing tons of gibberish. The Go team doesn't want to have yet another feature-bloated language, which is why I really enjoy it.

Anyway, there's over 7 pages of Go 2 proposals, and I've been searching through them, so I thought I'd list some of my favorites.

Interface Literals #25860

This one is a bit hard to understand at first, but once you understand it, it becomes much simpler to read.

So let's say you want to sort a slice of strings... This is now simplified with sort.Strings and sort.Slice, but for the sake of example, let's pretend sort.Sort is all we're able to use. This function takes an interface (called sort.Interface) which has 3 methods defined, so in order to use it, you must make sure those 3 methods are defined for your type:

But with this proposal, we can make it look like this instead!

Essentially what we have done is to use an interface literal instead of needing to make a new type! This means that the code that you use to sort can now be put where you actually do your sorting.

Add collect statements to the language #25626

This one is really nice, actually. They want to add a statement called collect to the language. It's hard to explain with words (in my opinion), so let's do it with an example!

Here is some valid Go code, with some error handling of course:

And as my Computer Science professor said, if you're repeating code, you're doing it wrong! But in Go, there's no good way to really handle the err != nil repetition problem here, so how could we help this? The collect statement helps fix this issue!

So let's have the same code, but with the collect statement.

So what does this do? Well the collect err indicates that we are going to collect into a variable err. Then, each time we assign to _!, it assigns that value to err, and checks if err != nil. If the assigned value is not nil, it exits the collect statement, and then the error handling can be done afterward.

My main issue with the proposal is that the syntax is a bit confusing (I personally don't like the _! syntax, I made a suggestion to use err! instead, which makes it a bit more clear).

Fix casing issues #1187

This one is quite a bit simpler than the last one. The os package has a lot of confusing casing, it's not always clear if the correct function call is os.Chdir or os.ChDir, or if it should be os.Tempdir or os.TempDir. I'm hoping the proposal to fix these casing issues gets accepted!


There are TONS of Go 2 proposals. Go has some really strong views on what should/shouldn't be in the language, the Go creators definitely don't want to make another C#, JavaScript, or other feature-bloated language. While I really like the idea of a collect statement, I'm not sure if the Go creators would like it, as it may be seen as an unnecessary feature (which I understand).

What do you think about the proposals? Do you have any you want to add? Let me know, Go is one of my favorite languages out there, and I'd love to have more insight as to what its future may look like!

Posted on Aug 14 '18 by:

deanveloper profile

Dean Bassett

@deanveloper

Computer science major at Michigan Technological University, class of 2020 👀

Discussion

markdown guide
 

Nice little collection you've got there. I agree that the casing issues should be fixed, although this shouldn't be a high priority since you can always set up your editor to auto-complete this stuff for you.

I'm meh about interface literals to be honest. Making the sorter a separate type makes it more re-usable in my opinion.

The collect statement is sort of try-catch again, isn't it? The problem with these kind of catch-all statements is that developers end up writing hundreds of lines of code in the block, and then you are left wondering about where exactly the error was encountered (especially when using error tracking software such as Rollbar or Sentry)

 

I really like the idea of interface literals, mainly for the reason that it groups code where it gets executed. It is nice to force re-usability though.

I agree with the critique on collect though, and I personally don't think it's going to go through. I know a lot of people are unhappy with Go's error handling for several reasons though, so I personally think it may be a step in the right direction, although I'm more of a fan of including github.com/pkg/errors into the standard library, which is a separate proposal. (I didn't include it in here since I believe it could instead just be added in Go 1)

 

Then, each time we assign to _!, it assigns that value to err, and checks if err != nil. If the assigned value is not nil, it exits the collect statement, and then the error handling can be done afterward.

...
...
...

congratulations, you just invented try-catch, which is a goto in disguise. truly everything new is old )

when I was inspired by golang (and node I guess)and wanted to do go-like error handling in php, I proposed to add conventional first argument, passed by reference to accumulate errors

after few functions called one can check if any errors occured and then decide what to return from calling function (maybe propagate errors further up the stack)

it was, of course, more of experiment than a complete solution, but it gave an alternative view on exceptions. Exceptions are gotos:
if first function in try block throws, you magically avoid calling second function which is, very likely, acceps value returned by first one as input. So exceptions are making your code obscure and extremely hard to understand. Actually, control on what happens in calling function is now in called function - just throw there and you force client code to obey and go to catch block, even though client code might wanted to do some more calls. Oh you really want to make sure you do that? Welcome to try-with-resources and finally and all sort of even more obscure stuff )

Instead it would be logical to admit that every time you call a function that would otherwise throw, you should really check error returned from this function and then branch your control flow with if statement - this way calling function is back in charge of what is going on. As a result code will look ugly in most of the languages. How to solve this - Im not sure yet, Optionals in java seems to be my next wearpon of choice - most of methods I write are now a chain of Optional.ifPresentOrElse() calls. Pretty declarative, I like to think of myself as a developer who uses functional style, who doesnt?))

Im still in search for THE solution (Im looking at you, monads), but my point here - question everything until you got to the core. Dont run around in circles.

 

While it's similar to try-catch, collect is a lot more explicit as to what is going on. I'll admit that it's the least favorite of the 3 I showed, though.

 

can you elaborate on how it is more explicit? it is really stopping execution of collected code block and jumps to if err != nil block?

edit:
looks like it is,
Whenever _! is assigned to, an implicit nil check is performed, and if _! is not nil, the block ceases execution.

from original proposal desription

doesnt look differrnt from throw-catch like semantics of other languages

When I say it's more explicit, I mean that in a typical try-catch, there's absolutely no indication as to where the error came from. This at least shows which places the error could have came from. Again, it's not my favorite thing in the world, but I'm not entirely against it

 

If I were to propose collect, I’d better have:

func SomeBigFunctionWithCollect() (int, error) {

  collect(err) {
    fmt.Println("Error in SomeBigFunction:", err)
    return 0, err
  }

  var i int

  _! := SomeErrorProneFunction()
  _, _! = AnotherFunc()
  // ...

  i, _! = LastFunc()

  return i, nil
}
 

Your code reminded me of my old library, mustbe. With mustbe, the code looks like this:

func SomeBigFunctionWithCollect() (i int, err error) {
  defer mustbe.Catched(func(e error) {
    fmt.Println("Error in SomeBigFunction:", e)
    err = e
  })

  mustbe.OK(SomeErrorProneFunction())
  mustbe.OKVal(AnotherFunc())
  // ...

  i = mustbe.OKVal(LastFunc()).(int)

  return
}

It's a bit more verbose, but it works already in Go1. Under the hood it uses panics, of course.

 

But we all understand that this is an ugly hack and basically you are fighting with a language syntax in the place where it should be your best friend.

Also, panic as goto reminds me of FORTRAN control flow :)

 

I like that syntax, although my main issue is how it would treat multiple select statements. I had mentioned using err! instead of err. Maybe even err^ since it's moving "up" to the collect statement. Syntax isn't a huge issue and can always be figured out after semantics though :)

 

Syntax is indeed a huge issue, since all the semantics was basically figured out ≈70 years ago by the guy named Alan Turing. AFAIK, Go is Turing-complete, so all we are talking about is syntax.

Go syntax was created to lower the entry threshold and learning curve. It is neither concise nor succinct and I am pretty sure it’ll never be because it is not intended.

In Haskell we have monads for that, in Elixir there is when, in Javascript promises might play that role, in Ruby and Python we early return. Go is doomed to live with multiple copy-pasted ifs and this is how it was designed.

 
 

Of the three here, I'm personally a fan of the interface literals; one of the themes in the comments (and in general Go-space) seems to be in favor of really writing everything out explicitly and I tend to agree until it comes to the boilerplate of implementing an interface, particularly in unit tests. I do like the brevity in this case.

Personally, I can't say I'm onboard with collect although I do agree the ever-present if err != nil { ... } does get a little tiresome. Generally I like clustering that error handling logic as close to the function call as possible, especially with the cases like:

if _, err := func(); err != nil {
   // do stuff here
}

And yes, consistency in the casing would rock my socks off!

 

I'm a fan of fixing the casing issues, but not too big of a fan of the other two. Mainly because it feels like those two proposals add syntactic sugar in order to achieve brevity. I do believe verbosity is one of the reasons I like Go so much. You spend most of the day thinking, not writing code. Making it easier to read and understand is a plus for me. Having more constructs does add to more confusion while reading IMHO.

 

I totally agree with verbosity being important in Go. I think the collect is my least favorite of the three, I personally don't mind the if err != nil pattern that much. I like the idea of interface literals though, I feel like needing my code to be separate from where I use it is a bit much.

 

Not a fan of the collect statement either.

The point of handling different errors is that you often have to do different things based upon where the code errors.

Bundling them into a single return delegates the responsibility (and understanding) to somewhere or someone else

 

More consistency in the stdlib will be very welcome, but really this is not per se Go 2.

I don't like the collect. Right now, using a special function, you can handle all error cases except for when you need to break out of a block or function. A label, which nobody uses, could be used in a closure to skip to the end, but it is ugly. If there was a function to jump back two steps in the stack rather than just one and you have your tidy, structured and most importantly, short error handler. Collect doesn't even address this and if you didn't know you can make one line error handlers already except that trigger a break... A collect statement is far less obvious than breakon(err) or so, and a full handler function can also be written, and it could be completely up to you how to handle it.

Personally, apart from mandatory system specific, 'difficult' to write from scratch algorithms, I often don't even use stdlib that much.

If there was one thing I would like, that would be the option of curly blocks or whitespace delimited a la python. Just look at a typical function in Go. The opening parenthesis distracts (from human scripts we more usually use a : to head a subsection, like Modula and Oberon and go switches)... And then gofmt leaves the closure parenthesis on an otherwise empty line. Gofmt almost already forces the correct indentation anyway (it only allows open and close parentheses on closures very short if blocks).

Other than that, I would like a more concise but unambiguous closure, though on a github issue I posted, someone pointed out the increased ambiguity.

Personally, I will be disappointed if any changes that make the version two are not profoundly powerful, like for statements and strict static typing and interfaces combined.

 

Something I might want to add:

proposal: Go 2: add become statement to support tail calls #22624

Tail calls allow for much faster recursion at the cost of reduced debug-ability. Might make a second post with some others that I may have missed! (Generics, other error handling solutions, etc)

 

Go finally gets generics?!

 

Not necessarily! Remember that all of these are just proposals.

 

I like the idea of collect, but the readability takes a nose-dive with that weird underscore syntax; I like the idea of err!.

 

There's a new error handling proposal out there that's receiving a lot of criticism. I'll probably make a post about it soon

 

Nice.

That said, I don't love the added syntax of #2. I proposed a simplified alternative here though:

github.com/golang/go/issues/25626#...