Go: The Diamond Problem

Posted on Wed 12 July 2017 in Go

I've witnessed some of the online debate when it comes to Go and OOP. Some people say the language is object-oriented, others don't share that view. I think it's safe to say that Go isn't a traditional object-oriented language.

Lately, I've been trying to learn more about Go from an OOP perspective - It's what I have most experience in. In fact, all of the languages I have worked with on a professional setting follow typical OOP standards (until now!).

After diving into structs and interfaces, as well as concepts like embedding (inheritance?), I was left wondering: How does Go solve the diamond problem?

The "diamond problem" is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C. If there is a method in A that B and C have overridden, and D does not override it, then which version of the method does D inherit: that of B, or that of C?

Ok, so Wikipedia has a pretty clear explanation on how Go handles it:

Go prevents the diamond problem at compile time. If a structure D embeds two structures B and C which both have a method F(), thus satisfying an interface A, the compiler will complain about an "ambiguous selector" if D.F() is called, or if an instance of D is assigned to a variable of type A. B and C's methods can be called explicitly with D.B.F() or D.C.F().

Essentially, Go 'solves' the problem at compile time: It just won't allow you to do it. The method has to be called explicitly or not at all.

Personally, I think this is a good solution. It avoids ambiguity and confusing code - There is already plenty of that going around!

A practical example (also available at play.golang.org):

package main

import "fmt"

type A interface {
    F()
}

type B struct {
    A
}

func (b B) F(){
    fmt.Println("F() of B")
}

type C struct{
    A
}

func (c C) F(){
    fmt.Println("F() of C")
}


type D struct {
    B
    C
}

func main(){
    d := D{}

    // Error! "ambiguous selector d.F".
    // d.F()

    // Allowed!
    d.B.F()
    d.C.F()
}