3 ms·
> what does happen from time to time is that f will look for custom methods on v that are at least logically equivalent to the static interface type f is declar
by epidemian 13y ago
> what does happen from time to time is that f will look for custom methods on v that are at least logically equivalent to the static interface type f is declared to expect.
Interesting. This kinda sounds good in practice, assuming programmers respect the interface's semantics when downcasting. But from a language design point of view it strikes me as a bit hacky al least, and at odds with the "Tell, Don't Ask" principle.
Couldn't this need for downcasting be avoided by allowing something like default method implementations in interfaces? I'm asking this without any practical knowledge of the Go language; i'm sure different approaches have been considered, but i'm interested in understanding the reasons behind the preference for downcasting :)
Imagine the Reader interface declared, besides the Read method, a WriteTo method with a default implementation:
type Reader interface {
Read(p []byte) (n int, err error)
WriteTo(w Writer) (n int64, err error) {
// Default implementation in terms of Read and w.Write
// using a buffer just like io.Copy
}
}
Then any type with a Read([]byte) (int, err) could still be referenced as a Reader, and when seen as a Reader one could call the convenient WriteTo method without downcasting. io.Copy could then tell the objects what to do instead of asking them what they are:
func Copy(dst Writer, src Reader) (written int64, err error) {
return src.WriteTo(dst)
}
A type that complies with the Reader interface could also implement a specific version of WriteTo if needed for performance reasons.
This way not only programmers could avoid resorting to downcasting, but the interfaces would become more explicit: instead Reader being "something with a Read method... and maybe a WriteTo if you want to, but this is not declared in Reader itself" it'd be "something with a Read method and a default WriteTo mehod".
The downsides of this would be that the dispatch semantics become a bit more complex for having to consider the default implementations, and maybe the interfaces could become more "bloated" (though i think that's subjective, as the semantics of an interface like Reader are implicitly mixed up with things like WriterTo; having all the methods in Reader would make that existing coupling more explicit).