| > In that case the code would break because it assumes too much. If the language was designed differently, people would code differently too. Sure, but we want interfaces to work the way they work now, because it's useful and reduces coupling. > This is like having a Java abstract class which "knows" all the possible subclasses in advance: if it breaks, it is the responsibility of that abstract class for having too much coupling. The analogy is not very apt, because in Go code the consumer is the one which is broken, not the producer. In the current design the consumer can make static assumptions about code, if those assumptions are helpful. This works because the assumptions don't change when new packages are imported. In other words, they work because the coupling is known and stable. You know exactly what a type is because its definition is only in a single package, the one you import. If you could define methods anywhere, you introduce hidden dependencies and more coupling. A new package might change the nature of the type. This is not acceptable. Of course people would write code differently if this weren't the case, but then the language would be less useful. > I am not sure I understand: if methods are defined in different packages, they have different qualified names, don't they? is there any ambiguity here? No, they don't have different qualified names. package foo
import (
"fmt"
"numbers" // type T int is defined here
)
type Hexer interface {
Hex() string
}
func PrintHex(v interface{}) string {
switch vv := v.(type) {
case Hexer:
return vv.Hex()
case numbers.T:
return fmt.Sprintf("%x", vv)
default:
return "unknown"
}
}
PrintHex(T(42)) will return "2a".Now what happens when you import (
"bar"
"baz"
)
where (in a hypothetical version of Go): package bar
import (
"fmt"
"numbers"
)
func (t T) Hex() string {
return return fmt.Sprintf("0x%x", t) // notice the 0x
}
and: package baz
import (
"fmt"
"numbers"
)
func (t T) Hex() string {
return return fmt.Sprintf("0x%X", t) // notice the 0x and CAPS
}
What will PrintHex(T(42)) return? "2a", "0x2a", or "0x2A"? |
> If you could define methods anywhere, you introduce hidden dependencies and more coupling. A new package might change the nature of the type. This is not acceptable.
This is the Expression Problem: adding new methods to existing types and/or implementing existing methods on new types. The intent is on the contrary to decouple things. You can use any feature to sabotage code.
> What will PrintHex(T(42)) return? "2a", "0x2a", or "0x2A"?
The compiler would warn you and take the latest definition into account, which is the one from "baz". That would be a pragmatic approach.
I notice that neither "bar" nor "baz" import "foo". So in your (hypothetical) example, Hex belongs to the global namespace, not "foo". I guess this is the case in non-hypothetical Go too? I didn't realize this, thanks.