Alternative Patterns for Method Overloading in Go

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.

thegoodthebadandtheugly

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/2017Here’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 –

  1. Loss of implicit type safety (why are you even using Go if you don’t see the benefits of static typing)
  2. 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!

21 thoughts on “Alternative Patterns for Method Overloading in Go

  1. Please have a look at https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis

    While he is specifically looking at API surface, it is basically the same discussion – Passing a configuration struct == your ‘object oriented’ method. Many functions == what you get if you punt on the problem.

    His answer is kind of a combination of your functional and object-oriented methods, and it DOES use a variadic function, WITHOUT the type assertion insanity.

    Methods or functions, built around a struct that has internal options, which take a variadic list of *functions* that when called and passed the struct, set those options. These functions would be defined in the same package as the Brew and CoffeeOrder, so would have access to private members.

    func Brew(options …func( *CoffeeOrder)) {
    }

    func ALargeCoffee(c *CoffeeOrder) error {
    c.shots = 3
    c.variety = “Arabica”
    c.cups = 1
    return nil
    }

    func ForTheOffice(cups int) func(*CoffeeOrder) error {
    return func(c *CoffeeOrder) {
    c.shots = 1
    c.variety = “Arabica”
    c.cups = cups
    return nil
    }
    }

    Brew(ALargeCoffee)
    Brew(ForTheOffice(4))
    etc…

    Except that being in-package, they would be more likely to be like :

    func Shots(s int) func(*CoffeeOrder) error {
    return func(c *CoffeeOrder) {
    c.shots = s
    return nil
    }
    }

    func Cups(c int) func(*CoffeeOrder) error {
    return func(c *CoffeeOrder) {
    c.cups = c
    return nil
    }
    }

    func Variety(s string) func(*CoffeeOrder) error {
    return func(c *CoffeeOrder) {
    c.variety = s
    return nil
    }
    }

    func PlainBlack(c *CoffeeOrder) error {
    c.shots = 1
    c.variety = “Arabica”
    c.cups = 1
    return nil
    }

    • Thanks Howard. I have read Dave’s post previously and I very much like the pattern he describes.

      This post is aiming to point out a very simple solution (primarily the first technique) to a simple problem of method overloading. The scenarios that I’m thinking of are simpler and more finite than the ones posed by Dave in his post. I personally feel that the first technique described is sufficient for a lot of the simple scenarios I’m thinking of and to do anymore would be over engineering in expectation of something that may or may not eventuate.

      If you foresee your requirements growing then of course you would choose Dave’s pattern but that’s not necessarily going to be every situation.

  2. Don’t forget the jQuery way:

    CoffeePlan().
    ForTheOffice(6).
    Organic().
    Brand(“Starbucks”)
    Scheduled(time.Add(time.Now(), time.Hour)).
    Brew()

    • Forgot . after Brand line.
      I occasionally use the chainable setters pattern. It can become multistep transformations across libraries. Using error propagation (errors passed through objects that shortcut a function) the calling code can be very clean while each worker function stays simple too.

      • I do like a chaining pattern if the implementation returns an immutable object (I believe this pattern originates from functional languages).

        Probably a post for another day, but the issue that I take with the chaining pattern is when it’s been applied to mutable objects. When I then used this pattern myself in a previous project (after being influenced by the design of third party libraries), when I came back to extend the code a month later I realised how difficult the solution was to work with as the mutating state became very difficult to manage / follow when it was passed around.

  3. Thanks for doing the write-up!

    Quick correction:
    Your first example for the CoffeeOptions version / OO version is returning the wrong type for the things that generate options.
    They are returning CoffeeOrder{} instead of CoffeeOptions{}, which cannot be passed to Brew()

  4. Nice writeup. One minor comment, on the template example the From* functions should be returning a signature of (string, []interface{})

    • Thanks Ray, I hope you found it helpful. Thanks for pointing out the typos in the code, I’ve fixed those method signatures now.

  5. Do you mind putting up the first example in a go play ground link? You are missing the Coffee struct I think

  6. Forgot .
    Quick correction:
    Your first example for the CoffeeOptions version / OO version is returning the wrong type for the things that generate options.

  7. The one thing I’m not a fan of is that the functions, in and of themselves, are hard to comprehend as they return (int, string, int)

    With that (functional) style, I’d probably prefer to make custom types to represent Cups, Variety and Shots, so an individual function would be standalone understandable.
    like when you read a git diff, or hover over a function in an editor.

    Example: https://play.golang.org/p/uQmpOpq1tB

    That said, I think I do prefer the struct config function approach as it leaves more room for extension later.

    • I totally agree David and I think having named return values are a good way to overcome that issue. Obviously my examples aren’t refined and hopefully readers already adopt such habits as named return values when they’re returning more than one or two obvious value types.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s