For example, http://play.golang.org/p/EmodogIiQU
type A struct { }
type B struct { A } //B is-a A
func save(A) { //do something }
b := &B{}
save(b); //OOOPS! b IS NOT A
If Go had is-a relationships, the code above would be valid. Instead, Go only implements has-a relationships, and simply provides shortcuts to calling B.A.foo() as B.foo().One could create B.save(), which would call save(b.A), but the very reason you're now proxying the call to save() is because there is no is-a relationship in Go.
We all know about interfaces, but the problem is that is-a relationships do exist, and you can't always use interfaces, because often you want to share the data encapsulated by the objects, not only the behavior. One ends up creating methods to fetch each piece of data, but in code that is supposed to be performant, calling methods instead of accessing fields is suboptimal.
1) Very shallow inheritance trees that could have very easily (and more logically) been replaced with interfaces. For example, NSResponder being everything's superclass even though just about all of its methods are empty implementations ("subclassers responsibility"), aka, it clearly should have been an interface.
2) Confused/strangely complex is-a relationships (mutable array is-a immutable array, what? so if I specifically specify NSArray, I may still get a mutable array and the type system will be happy???).
3) Strange rules around what methods are overridable, and more importantly, how specifically they can be overridden. Why can't I in a UIView subclass override -subviews to ensure that it always has the same subviews? Well, implementation detail, that's why. Normally this would be fine, but since anyone is allowed to muck around in a subclass, suddenly I need to know the way it was implemented. This of course conflicts other parts of the framework where you are definitely expected to override the method and not use a setter.
I have yet to be presented with one of these "killer" is-a relationships that must exist. If the sole excuse is "performance", then sure I'll conceded. I guess I don't work in environments where member access is the performance bottleneck so I guess I can't relate.
To my knowledge, nobody has found any better design than this, though some have found worse ones (e.g. HTML and its bizarre input element).
To your specific points:
1. Yes, NSResponder should definitely have been an interface. See for example the weird way that documents and delegates are spliced into the responder chain.
2. The fact that NSMutable* is-a NS* is a lovely design. Mutability can be understood as adding setters to a class that doesn’t have them. The alternative seems to be weird duplicative splits like ArrayList/Array, or String/StringBuilder/CharSequence.
3. Agreed; it’s a failing of ObjC that it’s usually unspecified whether a method is designed to be called, to be overridden, or both. I wish this were enforced at the language level.
The flow tree (render object tree) in Servo (or any other browser engine) must use inheritance: we have a heterogeneous tree of objects that all share a common set of fields (position, intrinsic widths, collapsible margins, some various bits that store state during reflow), but they all use virtual methods because they must lay out their contents differently.
We can't use composition because we wouldn't get virtual methods. We can't use an interface because then we would be forced into virtual dispatch for all of those fields that are shared between flows.
Rust doesn't have OO yet either, so we're forced to hack around it in weird ways (usually via a small amount of unsafe code to simulate inheritance).
> I have yet to be presented with one of these "killer" is-a relationships that must exist. If the sole excuse is "performance", then sure I'll conceded. I guess I don't work in environments where member access is the performance bottleneck so I guess I can't relate.
A browser engine is exactly that sort of environment. Forcing all member access to go through virtual dispatch would murder the performance of any browser.
Note that this was exactly the sort of thing that OO was designed for in Simula: heterogeneous trees of objects that all share some common fields but have different virtual methods. This generalizes to GUI libraries, game worlds etc—in short, simulations :)
† neither of which exist, since there's no objects and no inheritance. In simplifying things, this brings a constraint. Given how method resolution is a pain point WRT implementation complexity and performance (see e.g Ruby), this is a reasonable tradeoff. I am glad we have such an interesting choice of languages.
No need for methods, is performant. I can't think of a reason--besides extra typing--why this wouldn't be sufficient.
But it's not sufficient if what you want is to treat a B as an A. There is no is-a, so you have to keep treating a B as something different from an A.
type Saveable interface {
...
}
func save(a Saveable) { .... }
save(a); // ok, assuming A is Saveable
save(b); // necessarily ok, given than A is SaveableImplementation inheritance is a property of _some_ OO languages and one that is hard to imagine separate from OO. Which is why perhaps so many insist upon it being a required property for some language to be called OO. I am firmly in the camp that thinks implementation inheritance is a bad idea and it is best to avoid it even in langauges that support it. Thus I don't agree with anyone who claims that it is an important characteristic of a language.
Whether object instances find their genesis in classes, factories or prototypes are IMO the least important aspect to consider when discussing whether or not some language is truly OO. It's the object instances that do the important work. Where they came from is not so interesting.
Make it dynamic polymorphism, and I agree.
http://en.wikipedia.org/wiki/Subtype_polymorphism
http://en.wikipedia.org/wiki/Dynamic_dispatch
Polymorphism is a feature of the type system, and thus inherently static. Dynamic dispatch is something you often wind up with as a consequence of subtyping, but neither requires the other, strictly speaking.
Class is a single construct used for three different abstractions, namely:
- modularity/hiding
- inheritance
- polymorphism
This eventually turned out to be a bad idea (as evidenced by all the mess with virtual methods, multiple inheritance and structural patterns), and interfaces (and namespaces) were added to partly remedy this.
In Go, you instead get three orthogonal constructs:
- modules
- embedded structures
- interfaces
These directly correspond to basic principles of OOP. Nice and clean.
type Shape interface {
Area() int
}
type Square struct {
sideLen int
}
// Somehow denote that this function is implementing
// Shape's Area function
func (s Square) Shape.Area() int {
return sideLen * sideLen
}
Because, one of the things I like best about Go's interfaces is that you don't have to do that. type Person struct {
Name string
Address Address
}
is equivalent to type Person struct {
Name string
Address
}
And in fact, these are equivalent too: p.Address.Zip = "01313"
p.Zip = "01313"
http://play.golang.org/p/aKH3YxT5MbGo doesn't really support is-a for structs, as pointed out elsewhere in the comments here. Interface implementation is the only way to get the sort of "this type can be substituted for this other type" idea that is-a inheritance provides in other languages.
I would say that similar objections would be made about Go calling it self 'object oriented' however I also don't know what is being asserted.
Go has many constructs that make abstraction easier, and that is what many programmers want out of the OO idea, so its fine. I'm sure there specific things that some people require before they will label something as OO. Is it a functional question or a religious question as to whether or not Go is Object Oriented?
For many folks, it's basically built-in language syntax for objects (data+methods) instead of using patterns, idioms, and conventions. The object-oriented nature is explicit with syntax of language keywords instead of implicit with code organization.
>Is it a functional question or a religious question as to whether or not Go is Object Oriented
I think it's more of a functional/pragmatic one and not religious. (I would substitute the word "religious" for "psychological" -- more on that in the next paragraph). If a language is designated as "object-oriented", I think it's reasonable to have some expectations that the programmer does not have to write idioms & patterns to emulate C++/C# type of objects.
That said, there are still psychological motivations for expanding "object-oriented" to describe what Go can do. The problem is that the term "object-oriented" has gained a lot of currency as something useful and desirable in the programming world. Therefore, if someone labels something (e.g. Go) as "not object-oriented", that has an implied judgement that Go is somehow "handicapped" and has less power than C++/C#/etc.
Ideally, all programmers would treat the following statements as something neutral and non-threatening: "Go is not object-oriented. C language not-object-oriented."
But since we can't (the psychology), we get articles explaining how C and Go are actually object-oriented after all. We do this, that, and the other thing, and voila, "C is object oriented."
But that is precisely the point of Go: it IS less powerful. It has no inheritance, no generics or templates, no macros or pre-processor, no raw pointer access, no exceptions etc. This is all completely intentional, and yes it will be a turn-off to many.
e.g. This discussion:
http://c2.com/cgi/wiki?HowObjectOrientedIsClos
[NB I now just tend to think whether something is useful, leaving ideological purity to others.]
Do you program with anonymous values without names independent of their structure, and you can then reason about them equationally? If so, then you have values and probably lambdas to plumb them through the program as they lack their own behavior (you can have lambdas over objects also, but we don't call that functional these days).
Does the language encourage you to think about named entities or does it encourage you to reasoning about values? Of course, not many languages beyond Haskell try to push you to reason about everything as values. And most OOP languages include some form of values these days (if not, we definitely program with immutable objects like points that lack names/identity and might as well be called values).
This is also why OOP is necessarily tied up with state: it doesn't make sense for an immutable container of something to even have a name as it can be only really be identified by its structure.
So I want a few different, but similar things. These are actually stages in a processing pipeline, each stage doing different processing steps.
What I'm currently doing, which mostly works well, is to have a struct type ('Stage') which does all the generic work (equivalent to an abstract base class in C++). The Stage contains a function ptr ('Each') to actually do the processing step.
I can then have various 'derived' types which embed 'Stage'. Each one is assembled via a ctor which sets up the 'Each' function ptr. Effectively this provides inheritance with method overloading for the Each function.
I also have an interface ('Stager') which is satisfied by the Stage type, and so consequently by all the derived types (since they embed Stage).
So, I seem to have most of the benefits of C++ abstract base class and 'inheritance' (of data and methods), including overriding of methods by subclasses (using explicit assignment to function ptr).
It feels pretty nice to work with. The main concern I have is the slight klunkiness of the Stage/Stager duality. I also don't think I'd like this if I had many overridden methods (I just have one atm).
Anyone care to comment on a better way to this or other critique?
I've done that a few rare times, and I think in some case it makes a clean solution. See [1] for an example.
However I don't think that's how people should do _all of the time_ when they try to force an OO model onto Go.
[1]: https://github.com/aybabtme/loghooks/blob/master/hooks.go#L2...
The code which wants to be generic is in the handling of things like setting up channels between stages, handling data passing between them and cancellation (shutdown).
I also want to support a layer of abstraction, where I can compose a graph of stages into a single stage.
The goal is to have a number of primitive processing stages and allow abstraction and composition to build more complex processing.
Basically I could move to a pure interface and associated functions (not methods). That would perhaps be more idiomatic go. It's just a slight shame that a bunch of code which lives to my mind slightly more naturally as a set of methods on a type gets promoted to top-level package functions due to the inabillity to define methods on an interface. But that's probably OK.
Who said a standard definition doesn't exist? From Alan Kay, who 'invented' object-orientation and coined the term:
> OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I’m not aware of them[0].
Yes, you can make the argument that the term has evolved in common parlance beyond what Kay originally conceived of, but it's silly to propose a "modern" definition of object-orientation and not at least mention the original definition.
[0] From a 2003 email: http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...
[1] Note that he does not mention Java, even though he write this during the height of Java's popularity: http://www.tiobe.com/index.php/paperinfo/tpci/Java.html
It may be "silly" not to mention the original, but in this context the original a distraction: the original definition would exclude pretty much every language we today tend to consider object-oriented.
Also I believe there may be a typo in your last paragraph
>while staying clear of the brittle mess than is inheritance
It should be "that" instead of "than", I think.
You can use objects in C, and there's numerous in-the-wild big examples. But you have to implement it yourself or use some library, and there isn't one "standard" for the language.
Go clearly does have some features designed to make objects a first-class element. Equally clearly there are some other languages that have "more" such features.
This post I'm making is merely descriptive; there's no positive or negative attached to these statements.
Go is definitely an object-oriented language. It lacks classes, but those aren't required. We might argue that structural subtyping isn't really very OO. But given Go's lack of generics, this is a moot point.
You can have structure members that are function pointers which is basically attaching a "method".
There's a good quote in the article about this under 'Inheritance Is Best Left Out' - James Gosling responds to someone asking what he'd change in Java in retrospect - “I’d leave out classes,” he replied.
Right, but that doesn't mean Go is not object oriented, it simply eschews inheritance for composition. Go also offers something else n lieu of inheritance that many other OO languages don't have, embedding.
Attached that behavior to the struct itself so that invoking a struct's "area" function could give any of several different behaviors depending on exactly what "rect" struct you have.
type Area interface {
area() int
}
though I'd note I'm deliberately just copying the article as written, as an "area" that's confined to an int is awfully weird.Note you can indeed just add that, and you're able to take "Area"s anywhere you like, and you can even do it in a different module entirely; nothing has to "declare" than a rect implements Area.
To forstall the usual next question, no, Go has no further overloading based on type or anything else. My personal advice if you want to use that a lot is to use a different language. I like Go, but I look on the people trying to use it for machine learning or matrix math or other intensely mathematical computational loads with a bit of mystification. It isn't what it's good for, and I see no sign the core devs even consider it a marginal use case (to say nothing of a core use case) and have no intentions of changing the language to make this easier. If you really, really need overloading for your core use case, pick something else. Go is built for the environments where overloading is generally dangerous and used by people to do excessively "clever" things on the server, not for the environments where it is necessary. (Or perhaps "environment", singular, since "intensely mathematical code" is the only such thing I know of; everywhere else I've ever seen it it's asking for trouble.)
You can overload in Go: see for example how a zlib compressor is implemented [0].
The gist is that you take a standard io.Writer, embed it in your struct, and override the Write() method. This way you have a new io.Writer you can use wherever a io.Writer is needed. This pattern is actually standard in Go (and I guess in other languages where interfaces are more important than implementations)
Or maybe I didn't understand ?
[0] http://golang.org/src/pkg/compress/zlib/writer.go?s=4340:439...