7 ms·
In Go, interfaces are used for dynamic dispatch. No need to the the whole ".MakeNoise() calls .myNoise()" dance, just implement ".MakeNoise()" on each type dire
by bheadmaster 3y ago
In Go, interfaces are used for dynamic dispatch. No need to the the whole ".MakeNoise() calls .myNoise()" dance, just implement ".MakeNoise()" on each type directly:
type Animal interface {
MakeNoise()
}
type Dog struct{}
func (Dog) MakeNoise() {
fmt.Println("Woof!")
}
type Cat struct{}
func (Cat) MakeNoise() {
fmt.Println("Meow.")
}
func MakeNoises(as ...Animal) {
for _, a := range as {
a.MakeNoise()
}
}
func main() {
d := Dog{}
c := Cat{}
MakeNoises(d, c)
}
- ThePhysicist 3y agoThat's not what I described above. Object-orientation means that you can override behavior of a base class in a derived class and have the base class be "aware" of that overridden functionality at runtime. You could probably argue that interfaces allow composition and thereby an object-oriented style, I don't think that's what most people would understand as object-oriented programming, though.
- bheadmaster 3y ago> Object-orientation means that you can override behavior of a base class in a derived class while maintaining base functionality, and have the base class be "aware" of that overridden functionality at runtime. No, that's not an inherent property of OOP, that's a property of C++. To be OOP, you must support inheritance (which Go does, through struct embedding) and polymorphism (which Go also does, through interfaces). The polymorphic inheritance you're talking about is strictly a C++-specific implementation detail.
- ThePhysicist 3y agoStruct embedding is simple composition, not inheritance. If you embed an "Animal" in a "Dog" struct, the "Animal" struct doesn't have any means to find out it's a "Dog", i.e. it can never find out at runtime that it's embedded in a "Dog" object or rely on functionality defined in the "Dog" object (unless you write specific helper functionality for that). You can do Golang-style composition via union structs in C as well, but that doesn't make C an object-oriented language. Of course you can build a vtable in C or Golang to make object-orientation work (C++ came to being like that), but again it's not a concept the language itself supports.
- deleted 3y ago[deleted]
- bheadmaster 3y ago> Struct embedding is simple composition, not inheritance. In C++, composition differs from inheritance because when you embed an object inside another object, you cannot call its method directly, but through a wrapper: class Dog { Animal a; bool IsAlive() { return this->a.IsAlive() } } However, in Go, you can call their methods directly: type Dog struct { Animal } func main() { Dog.IsAlive() } So the distinction itself isn't really meaningful. In Go, composition is inheritance. How else do you define inheritance, if not as ability to call methods of the inherited type? > If you embed an "Animal" in a "Dog" struct, the "Animal" struct doesn't have any means to find out it's a "Dog", i.e. it can never find out at runtime that it's embedded in a "Dog" object or rely on functionality defined in the "Dog" object I'm not quite clear on what you mean. How exactly does this work in C++? How do you "detect" it's a Dog on runtime, or "rely" on functionality defined in Dog? If you can provide code example, that'd make things much easier for me to understand.
- JonChesterfield 3y agoC++ has that syntax under the name "unnamed" or "anonymous" fields. It's trivial syntactic sugar. That's unlikely to be called inheritance though.
- ThePhysicist 3y agoIf you redefine OOP as functional composition then almost every language passes as object-oriented, including C. To me that simply doesn't make any sense though, and I don't think it's what people would understand as object-oriented in the context of a programming language. Inheritance is a two-way relationship, i.e. if "Dog" overrides functionality in "Animal" then an internal method defined in "Animal" that calls that functionality would be dispatched to the overridden one defined in "Dog". That simply doesn't happen in Golang, hence it does not implement inheritance. C++ e.g. uses vtables for that.
- noduerme 3y agoThis is really strange, since pretty much all modern languages bundle structural inheritance and polymorphism together; it's definitely not just a C++ oddity, it's a pretty fundamental aspect of Java, JS, PHP, Python, ... I suppose polymorphic inheritance bundled together isn't a requirement of OO-style coding, but without it you have no concept of scope and without that you really can't say a language itself is Object Oriented.
- bheadmaster 3y ago> it's definitely not just a C++ oddity, it's a pretty fundamental aspect of Java, JS, PHP, Python, ... Yes, but I'd argue that's because all those languages just blindly copied what the massively successful C++ was doing at the time. Nobody gave it a second thought. From the perspective of language capability, polymorphic inheritance doesn't give you anything that you cannot achieve with polymorphism and inheritance alone. In fact, there was no polymorphism outside of virtual methods in C++. If you want polymorphism, you have to implement it through virtual methods, there's just no other way.
- noduerme 3y agoI don't speak Go. Is there some magic here that lets Dog and Cat know that they're implementing Animal? Or are you just informing MakeNoises(as ...Animal) that it will be receiving something that should implement Animal, with no compile-time type checking? Being able to specify that an argument conforms to an interface is not what's generally meant by "object oriented". Interfaces are really only a formality. What matters is inheritance.
- rakoo 3y ago> Is there some magic here that lets Dog and Cat know that they're implementing Animal? No there isn't and that's the point. Say you're building a lib and someone else wants to reuse it and its methods and extract functionality into a more generic interface; as the original author you don't need to go and modify your lib to accommodate for every single third party dev out there. Basically, the interface might be in another package, and that's fine. > with no compile-time type checking? The compiler will still check if Dog and Cat implement the interface, because at compile time everything is known.
- noduerme 3y agoBut then Dog and Cat need to both contain [edit] explicitly reproduce, verbatim [/edit] all the common functions of "Animal" - even if they're exactly the same? Just so someone later can take Dog and reuse it without implementing Animal?
- cube2222 3y agoYou can use struct embedding to accomplish that if you need it (Embed Dog / Animal in a new struct which adds/modifies functionality). Though this is used fairly rarely in Go.
- rakoo 3y agoAnimal is an interface: it has no "common functions", they're just definitions. If you need a common function, what you want is another structure, say Pet, and use structure embedding type Pet struct { func IsCute() bool {return yes} } type Dog struct { Pet # note how it is not named } func (Dog) MakeNoise() { fmt.Println("Woof!") } func main() { d := Dog{} d.IsCute() # yes # A third party lib type Chihuaha struct { Dog } c := Chihuaha {} c.IsCute() # yes c.MakeNoise() # Woof! }
- jonathanstrange 3y agoInterfaces do not provide inheritance either. A language without inheritance in my opinion isn't OOP. Otherwise, you can call C an OOP language, too, because you can write C programs in OOP style. Some people do that but it's not a reasonable way to define OOP. Structure composition is not inheritance. I personally wished Go had full OOP, ideally with multiple dispatch. It's what I miss most from CommonLisp and Racket when I write Go. The only arguments against it I've seen so far were rather silly and patronizing arguments along the lines of "we have to protect the programmers."
- quietbritishjim 3y agoA language is OO if it supports two things: * Encapsulation: Bundling data and functions syntactically together (in which case they're commonly called classes and methods) * Dynamic dispatch: Calling a method may call a different function implementation depending on the runtime type of the class instance But that's it. C++ inheritence is actually two features (at least), which you seem to be conflating: it supports dynamic dispatch through virtual methods, but also allows using the implementation of base class methods in derived classes (whether virtual or not). But only the first part is actually needed to be called object oriented. Even VBA is technically OO because its use of COM interfaces allows dynamic dispatch - even though it doesn't support inheriting method implementations at all. (A couple of notes: If a language only supports encapsulation but not dynamic dispatch then it's "object based" rather than object oriented. And, when encapsulation is supported, the language usually also supports data hiding (that's public/protected/private in C++ speak) but technically that's a separate thing.
- jonathanstrange 3y ago> A language is OO if it supports two things Yes, but only in your personal opinion. I disagree. As I've said clearly, for me a language is not OOP without inheritance. Go is not an OOP language. This is important for me because I like Go and think it's a great non-OOP language. In contrast, if Go was an OOP language, then it would be a really bad one - no traits, no mixins, no classes, no multiple inheritance, no multiple dispatch, no true dynamic method dispatch (see below), etc. Besides, treating Go as if it was an OOP language often leads to horrible Jave-in-Go programming style. Btw, I wasn't talking about C++ inheritance, I had things like CommonLisp CLOS and Ada's tagged types in mind. CL's OOP is among the most powerful because it has multiple dynamic dispatch and a metaobject protocol. Go lacks both, and it also doesn't really have dynamic dispatch. Dynamic dispatch means that superclass and subclass methods can be called at runtime depending on what is needed. You cannot call various superclass methods of a Go struct because Go structs do not have a type hierarchy/superclasses as there is no concept of inheritance. You sometimes get something that looks similar because of the duck typing (which is dynamic, but it's still not a full method dispatch) and because you can downcast at runtime using .(type). But that's still not the same as dynamic dispatch in an OOP language. Here is an explanation of the difference: [1] However, if you insist on imposing your definition as the one and only right one, then that's okay for me. In that case, Go is the most limited and crippled OOP language I've ever used, and so be it. [1] https://stackoverflow.com/questions/49114057/idiomatic-way-to-mimic-proper-dynamic-dispatch-in-go https://stackoverflow.com/questions/49114057/idiomatic-way-t...
- za3faran 3y agoThen you find real golang code littered with things like var _ Animal = (*Cat)(nil) var _ Animal = (*Dog)(nil) and you're back to the same place with more verbosity, and with much more difficulty when trying to figure out which types implement which interfaces. Much more load on IDEs as well.