I found the following criticism of Go language here: https://www.quora.com/What-reasons-are-there-to-not-use-Go-programming-language?share=1
Several points are mentioned, but the one that caught my eye is the following:
Types always implement interfaces implicitly; you just give your type methods that happen to have the same names, arguments, and return values as the interface in order to implement it.
Also, you can't overload functions or methods.
Neither of these two are particularly bad on their own. Indeed, either one on its own poses at worst a minor inconvenience. However, the consequence of having both in the same language creates the very real danger of being unable to use useful or possibly necessary libraries (e.g. your Go code needs to interact with some other service your company uses, and the only way to do that is with a Go library that team wrote).
The sneaky thing about implicitly implemented interfaces is that the interface's methods' names effectively transcend all namespaces. And that's particularly problematic due to Go's love of short, generic method names like Read() and New() and Create(). This is a global namespace name collision bug -- you know, the kind the rest of the software industry has been rid of for several decades.
For example, let's say I have two third-party packages, foo and bar. I want to make a type that implements both foo.Foo and bar.Bar.
foo.Foo is an interface that requires a method Copy() Foo,
and bar.Bar is an interface that requires a method Copy() (Bar, error).
Upon seeing this, I know I'm screwed. I can't overload a method called Copy() on my type, so I can never satisfy both interfaces. I simply can't continue, and for reasons that are entirely out of my hands. The best I can do is define a pair of types, one that implements foo.Foo and one that implements bar.Bar, and interveave their operation in some hideous, hacky way.
I thought this could perhaps be fixed by putting two structs that implement the two interfaces together in one struct, however doesn't quite work out nicely. See the following example:
package main
type Fooer interface {
Foo() int
}
type Fooer2 interface {
Foo() string
}
type DoFoo1 struct {
}
type DoFoo2 struct {
}
func (DoFoo1) Foo() int {
return 400
}
func (DoFoo2) Foo() string {
return "Foo"
}
type DoFoo struct {
DoFoo1
DoFoo2
}
var _ Fooer = DoFoo1{}
var _ Fooer2 = DoFoo2{}
var _ Fooer = DoFoo{}
var _ Fooer2 = DoFoo{}
func main() {
}
https://play.golang.org/p/Q6Vwpb019-6
The compiler complains:
./prog.go:33:5: DoFoo.Foo is ambiguous
./prog.go:33:5: cannot use DoFoo literal (type DoFoo) as type Fooer in assignment:
DoFoo does not implement Fooer (missing Foo method)
./prog.go:34:5: DoFoo.Foo is ambiguous
./prog.go:34:5: cannot use DoFoo literal (type DoFoo) as type Fooer2 in assignment:
DoFoo does not implement Fooer2 (missing Foo method)
In this case, _there is no actual ambiguity_, in because the signature of the two Foo functions is different. The Foo from DoDoo1 implements Fooer, and the Foo from DoFoo2 implements Fooer2. The code above should work. The compiler should automatically select the correct function based on the type signature of the methods to resolve the ambiguous name. Only if none of the candidates has required type signature, or if there are several with the required signature, then the compiler should raise an error. This would then also address the main criticism of interfaces I cited above.
This is not just a theoretical issue, there are indeed cases where it's hard to implement two different interfaces, normally from different packages, with one type, because there are name clashes.
_Before_:
The code above can be made to work by selecting a member of the DoFoo struct to satisfy one of the interfaces.
var df = DoFoo{}
var _ Fooer = df.DoFoo1
var _ Fooer2 = df.DoFoo2
_After_:
The compiler automatically select the sub-struct that correctly implements the interface.
var df = DoFoo{}
var _ Fooer = df // gets assigned to df.DoFoo1 automatically
var _ Fooer2 = df // gets assigned to df.DoFoo2 automatically
This would also work for functions with interface parameters.
I have bumped into this when using 3rd party marshaling libraries such as msgp which defines generic names for its interface methods.
For a suggestion like this, please identify real code that this would help. This issue has been recognized as a theoretical problem in Go since the beginning. It has never been changed because the problem is rarely seen in actual code. It's very unlikely that we would change the language in this way without specific examples where it would make a difference.
See also this FAQ entry: https://golang.org/doc/faq#overloading. The rule of matching methods by name is very simple and easy to understand. Matching methods by signature is much more subtle.
I mainly made this issue because I read said criticism, where someone apparently did run into this problem. I personally have not encountered this yet, but I thought that maybe for some Google projects you had already encountered this problem. I wanted to see if other people had really encountered the problem, or if it is, as you say, largely academic. So far, one person has replied that that they did run into this problem when using msgp. I will wait for more feedback on this issue, and if no one shows up with more examples of this issue, I will close it. You may label it "hold" or so if that makes more sense.
For the record I would like to state that after more thought, I found a workaround involving some pointers to the top level object:
package main
import "fmt"
type Fooer interface {
Foo() int
}
type Fooer2 interface {
Foo() string
}
type DoFoo1 struct {
*DoFoo
}
type DoFoo2 struct {
*DoFoo
}
func (d DoFoo1) Foo() int {
return d.FooerFoo()
}
func (d DoFoo2) Foo() string {
return d.Fooer2Foo()
}
func (DoFoo) FooerFoo() int {
return 400
}
func (DoFoo) Fooer2Foo() string {
return "Foo"
}
type DoFoo struct {
DoFoo1
DoFoo2
}
var _ Fooer = DoFoo1{}
var _ Fooer2 = DoFoo2{}
func main() {
df := DoFoo{}
df.DoFoo1 = DoFoo1{&df}
df.DoFoo2 = DoFoo2{&df}
var f1 Fooer = df.DoFoo1
var f2 Fooer2 = df.DoFoo2
fmt.Printf("%d %s\n", f.Foo(), f2.Foo())
}
https://play.golang.org/p/GCzx_OR9zEm
Like this we can simulate "overloading" the different functions as is appropriate.
As mentioned above, the current language rule is very simple and easy to understand: methods are matched by name, and nothing else.
Without examples of real code that would be significantly helped, we aren't going to make this change. Therefore, this is a likely decline. Leaving open for four weeks for final comments.
Seeing the lack of interest in this issue, and that a plausable workaround is available, I will withdraw this proposal immediately to leave the Go team more time to work on more pressing issues. Thanks for your consideration.
Most helpful comment
For a suggestion like this, please identify real code that this would help. This issue has been recognized as a theoretical problem in Go since the beginning. It has never been changed because the problem is rarely seen in actual code. It's very unlikely that we would change the language in this way without specific examples where it would make a difference.
See also this FAQ entry: https://golang.org/doc/faq#overloading. The rule of matching methods by name is very simple and easy to understand. Matching methods by signature is much more subtle.