Future of Golang
This is what I would like to see in the future of Golang. I am not a developer of the language, so take this with a very large grain of salt.
Before I tell you what I want (and do not want) I will give you the lens through which I view these arguments. I do not care how easy or hard it is to write Go code. I do care how easy it is to read and reason about Go code. I care very much that it remains difficult to be mislead by Go code. I acknowledge that Go is not perfect in this regard, but it is better than any other language I've seen to date.
Go 1.X
These are changes I would like to see that we could have without breaking backwards compatibility.
Packages Fulfill Interfaces
Packages already behave a lot like structs. They have methods and fields. The methods and fields are accessed with the dot operator. It would not be a stretch to be able to use a package to fulfill an interface. This would be fantastic for managing dependencies and greater decoupling.
Built-in Interface hasher
We have one built-in interface of "error", so there is precedent. Having another of "hasher" would be helpful. A value fulfills hasher if it can be hashed to a map key.
Go 2.0
These are things that would break backwards compatibility.
Field Attributes
I do not like the way field attributes are currently handled. It's one of the very free-form aspects of an otherwise static language. I would suggest they be replaced with key value pairs where the values can be any builtin type
type Foo struct {
Name string `json:"name"`
}
becomes
type Foo struct {
Name string {json: "name"}
}
And I would make these values available outside of reflect:
jsonName, hasField := foo.Name(:json) fmt.Println(jsonName, hasField) // "name"
Remove Complex
I don't think complex numbers should be a built-in type. I'm not going to make a long argument for this.
Better nil
Most of the rough edges around Go have to do with nil handling. There are a few things that I think would help with this.
No built in types should be nil. What is currently an nil slice would be a slice with length 0 and capacity 0. Same thing for a map. Under the hood it could still be represented as nil. As an extension of this, the zero value of a map would not fail when the first value is assigned.
The only thing that should have a nil value is a pointer. And a pointer should always have a type.
Type type
In a switch statement, we have foo.(type). I would propose extending that. This gets some of the functionality the community feels is missing from the lack of generics while keeping the language mostly static.
We could do type comparisons
var someBool = foo.(type) == bar.(type)
And often when using data-structures (like btrees) we want them to hold a consistent type. Having a Type type would let us write packages that enforced that, but with the same semantic rigor that interfaces give us.
Remove Named Returns
Named returns save a few lines at most and can cause confusion. They are almost universally recommended against.
Variables per loop with Range
https://github.com/golang/go/issues/20733
This can cause some bad headaches if you forget about it.
Range with functions, maybe?
Given a function
func(...) (..., bool)
It can be used in a range loop, and the loop will exit when the bool is false.
What I don't want
Generics
These are not necessary and they will not make code more readable. There are already lots of alternatives to generics and there's a good trade off that higher level abstractions require more work from the author to keep the code in a state that it can be reasoned about.
However...
I would make a suggestion to the community; don't use {}interface in your packages, except as a type declaration. Here's a simple example I've seen a few places (and written myself a few time); it is useful to couple a mutex and map. We you put that in package along with methods like Insert, Get, Iterate and so on. If we declare
type MapKey {}interface
type MapValue {}interface
Then it becomes trivial to setup a build process that copies that package and replaces those two lines. Yes, it's part of the build process, not part of the compile process and yes, now we have a bunch of generated code. But the code only needs to be generated when it's added or changed so it doesn't slow down compile times. And seeing generated code means you can confirm it looks they way you think it should and you can reason about. Remember that I want code that's easy to read. Code that's generated at compile time is impossible to read.