Cowboys draw their guns; artists draw their pictures; an artist cowboy draws pictures and guns. It happens more than you think, and name hygiene is a big deal in programming languages.
The problem goes away with less ambiguous naming conventions (e.g. DrawGun() and DrawPicture()), but overloading is so gosh darn convenient, and thinking of unambiguous names is quite difficult. C# is really the only mainstream language that gets this right with explicit interface implementations.
Yes, that's a drawback to implicit interfaces I had forgotten: it fails to solve the cowboy-shape problem (and worse, there's no way to fix it in the future without introducing a whole new kind of interface: the language seems kind of stuck with this issue).
Agreed about C# multiple interface handling. I'd really like to see something like that added to Go at some point.
In fact it's probably not that far away from where Go is now. Go already has something very similar for implicit composition / mixins. Now, if you do something like this:
type Foo struct {
Bar
}
Then Foo automatically has the methods of Bar, e.g. Foo.bar() instead of Foo.Bar.bar() although the former is really just shorthand for the latter.
If you then have:
type Foo struct {
Bar
Baz
}
Then assuming that Baz also has the method bar() then you now need to be explicit about it and call Foo.Bar.bar() or Foo.Baz.bar() because the compiler doesn't know which one Foo.bar() means.
Of course, this is slightly different to interface disambiguation, but there's no reason why you couldn't have two function definitions with the same name and some added interface qualifier - the functions would then only be callable if the object is cast to an interface. In Go, casting to an interface is a bit like boxing, as it creates a separate vtable for that object's methods to match the interface ABI.
Hence, you could have say:
a := Foo(foo)
b := Bar(foo)
Now a has a vtable entry for Foo::foo() -> a.foo() and b has a vtable entry for Bar::foo() -> b.foo().
On the other hand, there would be no foo.foo() method unless one were declared separately without any interface qualifier.
I think this is more of a problem in languages that also encourage having deep class hierarchies with virtual dispatch as their main mechanism of polymorphism. It's in those languages that you tend to see argument-less functions like Draw() because the entire concept of what it is for that object to be drawn as well as where has been baked in to one large composite object.
In languages that place more emphasis on type deduction and composition it's much more likely you're calling draw to tell it what to draw on, and the aggregate type information once you introduce an argument or two is actually quite rich.
Which leaves you with things like Close(), which, let's be honest, we don't need 30 interfaces that all just have Close in them just because the base language didn't happen to include one. The concept is simple and relatively unambiguous and almost always has to do with some kind of disposal of resource.
That's just how natural language is; languages based on objects will suffer since that can't rely on full natural language power even if based on naturalistic concepts. Any duck typed system simply doesn't care much about name meaning at all.
This is not an issue in any kind of real code written by a human being. When would you ever pass a cowboy object into a screen painting function?
You'd never do this:
cb := cowboy.New()
screen.Paint(cb)
Anyone who wrote that code would have to be insane, and anyone reviewing the code would tell the person they were insane.
Also, would you not test your code? Like, at least run it and make sure it doesn't do crazy stuff? Maybe write some unit tests?
There can always be edge cases where functions don't work precisely as you expect, but that happens without interfaces, too. Pass an array into a sorting function and it turns out to sort by string length not alphabetically.... the programmer bears some small responsibility for actually understanding what the functions that he calls actually do.
I agree that it's generally not such a big problem. It may be useful to draw a cowboy on the screen, but it's more likely that confusion could arise with methods that do something related, not something entirely different.
For instance, in JDBC there is a PooledConnection.close() method and a Connection.close() method. Both Connection and PooledConnection are interfaces. They are semantically related but it's not a polymorphic relationship. PooledConnection does not extend Connection.
PooledConnection.close() must always close the actual physical connection to the database because PooledConnection is used by the connection pool itself. The Connection interface is used by the application and hence Connection.close() may close the connection or return it to a connection pool.
JDBC drivers usually come with implementations of both interfaces where the Connection implementation wraps an instance of a PooledConnection implementation. Arguably, being able to formally declare which interface a particular close method belongs to is beneficial in cases like this.
I think this is really a problem that exists beyond interfaces. For example, even if you had these two concrete types, and both had close methods, how would you know when to call one close versus another and what they do? This kind of confusion is a perpetual problem... and I don't think Go interfaces make it any worse, really.
I would know because the concrete types formally reference an interface, and the documentation of the interface would tell me more about the intended semantics.
You're right that simply having this formal reference doesn't solve all problems that could possibly arise. But there's one form of confusion that is much less likely to arise.
As is so often the case, more flexibility comes with more opportunity for screw-ups.
The problem goes away with less ambiguous naming conventions (e.g. DrawGun() and DrawPicture()), but overloading is so gosh darn convenient, and thinking of unambiguous names is quite difficult. C# is really the only mainstream language that gets this right with explicit interface implementations.