TL;DR – In this post I share 3 patterns, 2 of which can be used, to deal with Go’s lack of method overloading – a functional way, an object oriented way, and a “JavaScript” way.

One of the things I miss in Go that I had in C# is method overloading. I realise that method overloading can be pretty badly abused but it’s perfect for default values (where optional arguments are insufficient) and sometimes when the method signature needs to be slightly different, for feature reasons, but the core reason for the function’s existence is ultimately the same.

In this post I’ll share 3 patterns to deal with this missing language feature. Two that I really like, a functional method and an object oriented method, and one that I seriously hate which I refer to as the “JavaScript” method.

The Functional Way

This method takes advantage of two really cool features in Go, it’s ability to return multiple values from a function and the ability to immediately accept those multiple values as an argument list for another function. Here’s an example for optional arguments –

(Update 24/05/2017 – Here’s a Go Playground sample written up by Jaime Martínez)

func Brew(shots int, variety string, cups int) []*Coffee { // Brew my coffee } func ALargeCoffee() (int, string, int) { return 3, "Robusta", 1 } func ForTheOffice(cups int) (int, string, int) { return 1, "Arabica", cups } func AnEspresso(shots int) (int, string, int) { return shots, "Arabica", 1 } func main() { myCoffee := Brew(ALargeCoffee()) coffeesForEverybody := Brew(ForTheOffice(6)) wakeUpJuice := Brew(AnEspresso(3)) }

As you can see in the calls in the main() func above, the code is really readable as we have nice names for each default list of settings. In addition, we can use this method as a way to separate responsibilities. Here’s an example –

func main() { t := FillTemplate(FromReader(myReader, tokens)) t = FillTemplate(FromFile(filename, tokens)) t = FillTemplate(FromURLWithJSON(templateURL, restServiceURL)) } // FillTemplate will accept a template and a slice of name-values and // replace the named tokens with the given values and return the result. func FillTemplate(template string, tokens map[string]interface{}) string { } func FromReader(r io.Reader, tokens map[string]interface{}) (string, map[string]interface{}) { var template string // Read from the reader and fill template with the contents. return template, tokens } func FromFile(filename string, tokens map[string]interface{}) (string, map[string]interface{}) { var template string // Read the contents of the filename and store it in template. return template, tokens } func FromURLWithJSON(templateURL string, jsonRESTURL string) (string, map[string]interface{}) { var template string // Read the web page at the given template URL and store the contents in template. var tokens map[string]interface{} // Read the JSON resource at the given REST URL and transform the JSON into a tokens map. return template, tokens }

In my opinion, this is the superior pattern to use in the absence of method overloading in Go. The first reason being it immediately makes your code more readable. In the example above we can see immediately on each line –

t initially holds a template filled from the contents of myReader

t then holds a template filled from the contents of the given file name

t then holds a template filled from the contents of the template URL with the JSON from the service URL

It also forces you to separate your logic and elegantly structure your code. In our example, the logic for parsing and filling the template is kept to the FillTemplate function. We’ve separated out all the necessary IO tasks to get the data to work with into the FromReader, FromFile, and FromURLWithJSON functions.

A third reason, which is more personal/emotional, is that I feel that it makes use of a powerful Go language construct and thus feels more “Go”-ish. Unless others feel the same way and this becomes an idiomatic alternative for method overloading within the Go community, this isn’t really a legitimate reason.

The obvious con to this method is that if we have a lot of parameters, the method signature can become unwieldy. This is where being a little object oriented could be a superior method.

The Object Oriented Way

If you’ve come from a language like C# or Java, I imagine this would be the go to alternative to method overloading. Here is an example for default parameters –

type CoffeeOptions struct { Shots int Variety string Cups int } func Brew(opt *CoffeeOptions) []*Coffee { // Brew my coffee using opt.Shots, opt.Variety, and opt.Cups } func ALargeCoffee() *CoffeeOptions { return &CoffeeOptions{3, "Arabica", 1} } func ForTheOffice(cups int) *CoffeeOptions { return &CoffeeOptions{1, "Arabica", cups} } func AnEspresso(shots int) *CoffeeOptions { return &CoffeeOptions{shots, "Arabica", 1} } func main() { oneLargeCoffee := ALargeCoffee() myCoffee := Brew(oneLargeCoffee) bobsCoffee := Brew(oneLargeCoffee) coffees := Brew(ForTheOffice(6)) wakeUpJuice := Brew(AnEspresso(3)) }

But the above example wouldn’t be considered truly OO for some people, so we can go one step further and create the object like so –

type CoffeeOrder struct { Shots int Variety string Cups int } func (o *CoffeeOrder) Brew() []*Coffee { // Brew my coffee using o.Shots, o.Variety, and o.Cups } func ALargeCoffee() *CoffeeOrder { return &CoffeeOrder{3, "Arabica", 1} } func ForTheOffice(cups int) *CoffeeOrder { return &CoffeeOrder{1, "Arabica", cups} } func AnEspresso(shots int) *CoffeeOrder { return &CoffeeOrder{shots, "Arabica", 1} } func main() { order := ALargeCoffee() myCoffee := order.Brew() coffees := ForTheOffice(6).Brew() wakeUpJuice := AnEspresso(3).Brew() }

In comparison to the functional method, for the examples I’ve offered above, I’d personally go for the functional method. In the first OO method above, in the main() func, the difference in usage is virtually none but now we have an additional struct that I feel is really unnecessary.

In the second example above, it’s definitely more OO, but we can’t always take this extra step in all scenarios. If the Brew() func was a method on a CoffeeMachine struct, we would have to use the method outlined in the first example. Ultimately the choice to use this OO method or the functional method is probably down to personal preference.

Where the OO method is probably superior is if we had a lot more options. Take the following functional example and compare it with the OO example after it.

Functional

func Brew(shots int, variety string, cups int, roastFreshness time.Duration, roastProfile int, origin string, isBlended bool, processingLevel string, altitude float64) []*Coffee { // Brew the coffee }

Object Oriented

type CoffeeOptions struct { Shots int Variety string Cups int RoastFreshness time.Duration RoastProfile int Origin string IsBlended bool ProcessingLevel string Altitude float64 } func Brew(opt *CoffeeOptions) []*Coffee { // Brew the coffee } func main() { opt := &CoffeeOptions{ Shots: 1, Variety: "Robusta", Cups: 2, RoastFreshness: time.Hour * 24 * 7, RoastProfile: 9, Origin: "Vietnam", IsBlended: false, ProcessingLevel: "Washed", Altitude: 1500.0, } coffees := Brew(opt) }

I think it goes without saying, the OO way is vastly more readable in the above examples and for that reason I would definitely choose the OO method in such situations.

The “JavaScript” Way

So this method SHOULD never be used, but surprisingly (and very annoyingly) I’ve seen it quite a few times in various Go projects on GitHub and in blogs. In fact the fact I have witnessed this method being used was a big motivation as to why I even decided to write this post. I list it here mainly so I can bag it out and point how horrid it is. Here is an example and I hope you feel as horrified about it as I was the first time I saw it in use –

func Brew(opts ...interface{}) { // Type assert each option in the opts []interface{} // e.g. if shots, ok := opts[0].(int); ok { // Use shots } // etc... }

The fact I have seen methods like this is mind boggling to me. The only sane reason I can deduce, as to why you would choose to do this, is that you have only ever known JavaScript prior to using Go. If this is you then I, on behalf of all Go programmers, forgive you for your past sins. But please for the love of all programmers that work with your code after you, use one of the two methods I’ve mentioned previously. Variadic functions have their place as a solution to some problems and it’s not in the same universe as the “solves lack of method overloading in Go” universe.

To make it clear, the biggest issues I take with this “solution” are –

Loss of implicit type safety (why are you even using Go if you don’t see the benefits of static typing) Misuse of variadic functions – in Go they are intended to be used for an arbitrary number of arguments, not an arbitrary number of signatures!

I really can’t stress enough NEVER TO USE THIS METHOD. If after seeing the rubbish code above you still feel it’s a good solution to function / method overloading in Go and feel like arguing your point in the comments below, don’t bother as I have no intention of responding-to or approving your idiotic view.

Conclusion

Although I clearly favour the functional method above, I do think you need to consider your particular scenario. In fact you’ll probably end up using a hybrid of the functional method and the object oriented methods. The key take away though, beyond two great alternatives for a lack of overloading in Go, is that you should NEVER USE VARIADIC FUNCTIONS FOR OVERLOADING!