Introduction
In the past few months I have been dabbling in the Go language, and found it to be wonderfully clear and concise. Coming from C#, a language which I still love, I would like to see three things added to the Go language:
- Extending existing types
- Operator overloading
- Default or optional parameters
Let’s look at them in detail
Extending existing types
When I was working on a project in C#, some time ago, I wrote a small library, which I turned into a Nuget package, extending some common types with some utility methods. Why? Because I found that I was writing a lot of boilerplate code, which I always try to avoid. As an example, I wrote this method in C# extending the Boolean type:
public static string AddNot(this bool variable, string keyword, string negation = "not")
{
return variable ? keyword : $"{negation} {keyword}";
}
For those of you not familiar with extension methods in C#, a short breakdown of this method:
- The method is static as this method does not need a specific class instance.
- “this bool variable” is what you would call the receiver in Go.
- keyword and negation are the two parameters, negation is an optional parameter, I will return to that later.
And you can call this method like this:
var result=false;
var resultString=result.AddNot("available");
The resultString variable would contain “not available”, it is easy as that.
Trying the same thing in Go:
type BooleanExtensions interface {
AddNot(string, string) string
}
func (b bool) AddNot(keyword string, negation string) string {
if b {
return keyword
} else {
return fmt.Sprintf("%s %s", negation, keyword)
}
}
You get the errormessage: “cannot define new methods on non-local types”
I know you can circumvent this by using type-aliases like this:
package main
import "fmt"
type extendedBool bool
type BooleanExtensions interface {
AddNot(string, string) string
}
func (b extendedBool) AddNot(keyword string, negation string) string {
if b {
return keyword
} else {
return fmt.Sprintf("%s %s", negation, keyword)
}
}
func main() {
result := extendedBool(false)
resultString := result.AddNot("available", "not")
fmt.Println("Available: ", resultString)
}
But this still feels a bit artificial, because of the type conversion needed. I found out that extendedBool can still be used in if-expressions for instance.
Perhaps this is conscious language-design choice, for a particular reason, but if it isn’t, I would like to see it added.
Operator overloading
Many languages, though not all support the notion of operator overloading.
For example, Go has built-in complex-number support, but if I wanted to add Quaternions for example?
Imagine this for example:
type Quaternion struct {
a float64
i float64
j float64
k float64
}
q := Quaternion{a: 1, i: 2, j: 3, k: 4}
fmt.Printf("%+v\n", q)
Now it would be nice if could scale this variable by two for example:
r:=q*2
Of course it is possible to define methods on the struct:
type QuaternionOperations interface {
Scale(float64) Quaternion
}
func (q Quaternion) Scale(scale float64) Quaternion {
return Quaternion{
a: q.a * scale,
i: q.i * scale,
j: q.j * scale,
k: q.k * scale,
}
}
And you write:
r:=q.Scale(2)
However, this feels a bit artificial. Again, this might be a conscious language-design decision, in which case I can live with it, but if it isn’t, it would be a nice addition.
Default or optional parameters
For this we will go back to the C# extension method I showed you in the beginning, and we will just look at the method-signature:
public static string AddNot(this bool variable, string keyword, string negation = "not")
I will not go into the “public static” bit, but you can see that that parameter negation has a sensible default value. And this is important when writing an API: provide the API users which both as much help as possible, as well giving them the ability to change and adapt as much as they want.
There are two ways to add this to Go
First method: C# like
We could have default parameters like we see here in C#. This would probably look something like this:
func addNot(keyword string,negation string="not") string
This doesn’t look too bad. The restriction is that the default parameter or parameters have to come at the end of the parameter list.
Second method: Method overloading
The second method would probably be better, that is by overloading methods. This would look something like this:
func addNot(keyword string) string
func addNot(keyword string,negation string) string
Also the same restriction would exist: optional or default parameter would have to be at the end of the parameter list.
A solution to those restrictions would be to have named arguments, but that is a discussion for a different time and place.
Conclusion
These are some of my thought. Go without these addition is a great language in itself: clear, concise, and a joy to work with (although a bit quirky at times). So, all these suggestion are nice-to-haves not showstoppers.
Leuk