Vladimir Vivien
Jan 27, 2016 · 4 min read

I have been writing code in Go (golang) full time for almost two years now (wow, time flies). Before Go, I spent my career writing Java code. So needless to say, I still carry a bit of the OO baggage. My Go code (and the code of many others in the community) can, unnecessarily, take on OO traits simply out of habits. In this writeup, I show that the impulse to reach for the heavy object-based model to implement the simplest of types is not always necessary. I start with an object, complete with attached methods, and show how it can be reduced to a simpler type all while keeping the same level of expressiveness in the code.

A Simple Pair Type

Go brings a rich set of data types from basic to composite. However, there is no language-supported class primitives. So naturally, some developers (myself included) tend to abuse the use of struct to overcompensate by implementing objects using structs and overlooking the rest of Go’s types. While I was hacking away at some Go code (Authomi) recently, I needed to create a tuple pair to store two related elements. Of course, my first inclination was to create struct based type, as listed in the following code snippet.

type Pair struct {
values [2]interface{}
}

func MakePair(k, v interface{}) Pair {
return Pair{values:[2]interface{}{k, v}}
}

func (p Pair) Get(i int) interface{} {
return p.values[i]
}

func main() {
p := MakePair("Hello", false)

fmt.Println(p.Get(0), " ", p.Get(1))
}

http://play.golang.org/p/RWuoAX5D0t

The code for Pair is simple enough and does the job. Type Pair is implemented as a struct composite type that wraps type [2]interface{} to store the pair of values. I also created constructor MakePair() and added a Get() method to conveniently retrieve the values. As I said earlier this works well, however, it has all of the hallmark of classical OO where the underlying type, [2]interface{} that stores the pair of values, is enshrined away inside another type, Pair. Functionally though, this implementation works quite well and in an OO language, such as Java, it would not be questioned.

In Go land, however, where simplicity reigns, its only natural to question the necessity of the level of indirection and the noise just to set and get two values. I decided to take another swipe and put the code on a diet. “Do I really need Get() method” I thought? No, not really! So, let’s remove it, as shown in the following snippet. The code still functions correctly and seems to keep its readability and semantics.

type Pair struct {
Vals [2]interface{}
}

func MakePair(k, v interface{}) Pair {
return Pair{[2]interface{}{k, v}}
}

func main() {
p := MakePair("Hello", false)
fmt.Println(p.Vals[0], " ", p.Vals[1])
}

http://play.golang.org/p/CG5fs66exA

The previous change keeps the code just as readable as the original program even after removing the Get() method from the Pair type (note Vals element is now exported). Next, I started to question the need for the constructor function MakePair(). As you can see from earlier, it simply hides the initialization of the internal array. Though useful, it is not at all necessary to create a Pair value. Next, let’s remove the constructor and see what the code looks and feels like.

type Pair struct {
Vals [2]interface{}
}
func main() {
p := Pair{[2]interface{}{"Hello", false}}
fmt.Println(p.Vals[0], " ", p.Vals[1])
}

http://play.golang.org/p/C4dxC4n9bc

Well creating a new Pair value is not as readable as it was before. Without the constructor function, creating the Pair looks like this Pair{[2]interface{}{“Hello”, false}}, we have lost some of the context provided by the constructor. Writing Pair{[2]interface{}{…, …}} every time we need to create a pair would get old quick. So, what else can be done to improve readability. One solution is to create a secondary type to provide a named reference to [2]interface{} as shown in the following snippet.

type pairVal [2]interface{}
type Pair struct {
Vals pairVal
}

func main() {
p := Pair{pairVal{"Hello", false}}
fmt.Println(p.Vals[0], " ", p.Vals[1])
}

http://play.golang.org/p/3SavItOBJN

The new type pairVal is defined as [2]interface{} which makes creating a new Pair a bit less mangled than before. Up to this point, we have been removing code and still ended up with the desired functionality for Pair. The last change, however, added to the code instead of taking away, which is not the goal of this exercise. Upon further analysis, it becomes quite clear that array type [2]interface{}, which represents the pair of values we need, can be used directly as the actual Pair type. This means, we can drop the struct type altogether, which was simply hiding the array, and end up with the following.

type Pair [2]interface{}func main() {
p := Pair{"Hello", false}
fmt.Println(p[0], " ", p[1])
}

http://play.golang.org/p/EVEUfos-WE

Conclusion

The final form of type Pair is far different from its original. We started with a full object-like model for the Pair type. However, taking advantage of Go’s fully featured type system, we are able to represent the Pair type as a simple named 2-element array that is just as expressive. Using the array directly, instead of a struct-based type, made it unnecessary to attach additional methods to express intent. We are able to use the array’s bracket expression as a way to clearly express intent with a familiar syntax.

I will admit, there are legitimate scenarios where modeling composite object-like types, using structs, make sense and sometimes necessary. However, as this write up showed, in many cases, removing the level of indirections, imposed by OO style implementations, and using the types directly can keep your code simple and just as expressive.

Content originally appeared in http://blog.vladimirvivien.com/2016/01/go-for-type-simplicity-in-go.html

Learning the Go Programming Language

Short and insightful posts for newcomers learning the Go programming language

Vladimir Vivien

Written by

Software Eng • Go Programming • Kubernetes • Author http://golang.fyi

Learning the Go Programming Language

Short and insightful posts for newcomers learning the Go programming language

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade