News for hackers. Stuff that matters.
Re:Pretending OOP Never Happened by throwaway894345 on Friday May 15, 2020 @02:11PM (#23195369)
I think you and I must be part of different bubbles, because I haven't heard anyone advocating for moving away from OOP toward procedural code. Looking at new languages which are proudly not OO (e.g., Go, Rust, etc), none appear to be advertising that you can write code that looks like 60s/70s procedural code--or at least not in the "pervasive global state" sense that you originally attributed to it. Instead, those languages tend to have first class functions and discourage global state.
Re:Pretending OOP Never Happened by cy_hauser on Friday May 15, 2020 @03:07PM (#23196093)
> "... languages which are proudly not OO (e.g., Go, ...."

Outside of class based inheritance which aspects of Object Oriented is Go missing? I code Go daily and consider it primarily an OO language.

Re:Pretending OOP Never Happened by throwaway894345 on Friday May 15, 2020 @04:20PM (#23196952)
In general, "OO design", which is to say if you're writing a BookStore application, there are no Book types with "sell()" methods that imply that book instances must have a reference to the payment service and everything else in the application in addition to their plain-old-data fields (note that this is another phrasing of the banana/gorilla/jungle quip). Inheritance and OO design are the distinguishing features of OOP in my mind; if you don't have these, then OOP is indistinguishable from FP or DOP. If Go is OOP, then which languages aren't, and why?
Re:Pretending OOP Never Happened by cy_hauser on Friday May 15, 2020 @05:41PM (#23197816)
> "Inheritance and OO design are the distinguishing features of OOP in my mind;"

I don't consider inheritance an important feature for a language to be an "Object Oriented Language." Inheritance: none, single, and multiple have been bandied about in the OO world for quite some time now so I understand where you're coming from.

I break out design from the language. While I wouldn't design very deep without knowing the language I was targeting I do consider Go as having a natural bias towards driving me to OO design over, say, functional or procedural. For me Go lends itself naturally to OO design. Sans inheritance, of course.

> "if you don't have these, then OOP is indistinguishable from FP or DOP. If Go is OOP, then which languages aren't, and why?"

Haskel, some Lisps, some Forths, etc. would be more non-OO languages in my mind. C is also a non-OO language to me even though you can certainly code it in an OO style.

I guess I agree with the blog author, OO is simply part of the language landscape these days and isn't going to be supplanted by functional any more than procedural was supplanted by OO. And with my mind-set then I'll say most all of Algol's recent children can be considered OO languages. So for me Go is definitely an object oriented language. But I now understand why you don't.

Re:Pretending OOP Never Happened by hnick on Friday May 15, 2020 @10:11PM (#23199866)
> I don't consider inheritance an important feature for a language to be an "Object Oriented Language."

I mostly agree except for one catch. How do you extend a class which is encapsulated properly? Often this is the only way I use inheritance because I need to do one or two extra things that rely on the internal state of an object which I can't modify directly. So I'm wondering if there is a good way that doesn't rely on modifying the implementation of the original object to be more flexible.

Re:Pretending OOP Never Happened by cy_hauser on Saturday May 16, 2020 @08:22AM (#23202811)
This is tough to answer without a language in mind. Depending on what access you have you can use interfaces to get run time method binding or composition to get compile time binding. For either of these you'll have to "wire" the needed types together. This is what Go has you doing.

Going back to the "old days" you'd reach for a pattern. Adapter pattern was the most common. Bridge or Facade maybe? But patterns fell out of favor a while back (imo) because they were tough on the coder's mind. A lot of languages added useful bits to create these pattern behaviors more naturally and without reaching for a book. I'd need to see code to be more specific.

Re:Pretending OOP Never Happened by hnick on Saturday May 16, 2020 @09:07AM (#23203078)
I'm not familiar with Go but apparently it has "embedding" which is a little different. Maybe that works. But it's largely playing the same role - a way to extend a class you might not control.

So I'd rephrase that I suppose, perhaps we don't need something called Inheritance, but we do need a way to extend objects we don't control without breaking encapsulation.

As an example if you had a method that outputs XML and you want JSON, you can use an adapter object to call it, read the string, and reformat and pass it through. That works (and I've done things just as bad or worse many times from necessity), but it's definitely more hackish and less efficient than a solution that uses object fields to output JSON natively. Inheritance is my familiar way to do this in cases where I don't control the code (in which case injecting a Writer class of some sort probably makes more sense).

Re:Pretending OOP Never Happened by cy_hauser on Saturday May 16, 2020 @11:19AM (#23204081)
Go's embedding _is_ composition (has-a) in any other OO language but with a little sugar. Embedding allows the compiler to optimize the memory layout for a composed object and allows you to reference the composed object's fields without a qualifier. For example, if a Student class has-a Address {city, state, zip} then you could refer to the city field by coding aStudent.city rather than aStudent.address.city. It's handy but doesn't otherwise change "normal" composition.

I'm not advocating against inheritance. If it works then go for it. I do when I'm coding in languages that have it. This stemmed from a thread discussing whether the lack of inheritance in Go was sufficient to exclude it from being an object-oriented language.

Re:Pretending OOP Never Happened by throwaway894345 on Saturday May 16, 2020 @05:06PM (#23207227)
> but we do need a way to extend objects we don't control without breaking encapsulation.

By definition, you can't extend objects you don't control without violating encapsulation. Encapsulation is all about controlling access so that the owner of a class can make changes without breaking downstream code. If downstream code accesses these private APIs, the downstream code may break, regardless of whether the access was via inheritance or composition (as a side note, "protected" makes no sense--who cares if downstream code breaks because it was accessed by inheritance but not by composition?).

> As an example if you had a method that outputs XML and you want JSON, you can use an adapter object to call it, read the string, and reformat and pass it through. That works (and I've done things just as bad or worse many times from necessity), but it's definitely more hackish and less efficient than a solution that uses object fields to output JSON natively. Inheritance is my familiar way to do this in cases where I don't control the code (in which case injecting a Writer class of some sort probably makes more sense).

You can do this just as easily without inheritance. You just need a way to access the member fields--whether you access them via inheritance or otherwise doesn't really matter. E.g.,

    class Foo:
        def __init__(self, x: int, y: int) -> None:
            self.x = x
            self.y = y

        def xml(self) -> str:
            return f"<foo x={self.x} y={self.y} />"

    # Via inheritance
    class JSONFoo(Foo):
        def json(self) -> str:
            return json.dumps({"x": self.x, "y": self.y})


    # Via composition
    class JSONFoo:
        def __init__(self, foo: Foo) -> None:
            self.foo = foo

        def xml(self) -> str:
            return self.foo.xml()

        def json(self) -> str:
            return json.dumps({"x": self.foo.x, "y": self.foo.y})

    # Simple
    def json_foo(foo: Foo) -> str:
        return json.dumps({"x": foo.x, "y": foo.y})
> I'm not familiar with Go but apparently it has "embedding" which is a little different. Maybe that works. But it's largely playing the same role - a way to extend a class you might not control.

Go's embedding is exactly composition. It doesn't let you access private member data of the embedded class. It's just syntax sugar for delegation. In other words, you could create the composition version of the JSONFoo class like this:

    type Foo struct { X: int; Y: int }

    func (f *Foo) XML() string { return fmt.Sprintf("<foo x=%d y=%d />", f.X, f.Y) }

    // The compiler will automatically generate this method:
    //
    // func (jf *JSONFoo) XML() string { return jf.Foo.XML() }
    //
    // And for a given instance of JSONFoo, the compiler will convert all instances
    // of jsonFooInstance.X to jsonFooInstance.Foo.X.
    type JSONFoo struct {Foo}

    func (jf *JSONFoo) JSON() string { return fmt.Sprintf(`{"x": %d, "y": %d}`, jf.X, jf.Y) }
Note that a `JSONFoo` isn't a `Foo`. This is an error:

    func printXML(foo *Foo) { fmt.Println(foo.XML()) }

    func main() {
        jsonFooInstance := JSONFoo{0, 0}
        // printXML(jsonFooInstance) // error
        printXML(jsonFooInstance.Foo) // correct!
    }
Re:Pretending OOP Never Happened by hnick on Saturday May 16, 2020 @09:11PM (#23208553)
Thank you for the examples. I've been in perl too long where a lot of good practice is just a suggestion and not enforced. In particular private vs protected (which is what I was thinking of regarding accessing internals) doesn't really exist.

I tend to lean towards inheritance for functional extensions and composition for everything else, but there are so many ways to skin an OO cat.

Re:Pretending OOP Never Happened by AnimalMuppet on Friday May 15, 2020 @08:25PM (#23199141)
I'll be blunt: That Book type design is horrible, precisely because of the problem you are complaining about. The answer is simple: Don't do it that way.

If that's what people think OOP is supposed to look like, no wonder they don't like it.

Re:Pretending OOP Never Happened by temac on Friday May 15, 2020 @09:17PM (#23199524)
I suspect "OOP" is teached like that most of the time. Or more precisely, this is teached as being OOP (as a fundamental distinguishing point from "procedural", most often -- ironically, the opposition "OOP" / "procedural" is then also complete garbage)

I've yet to find a non boring, yet descriptive, consensual alternate definition of OOP; the first thought people have is more Java than Smalltalk, and if you exclude inheritance and everything virtual, then it boils down to syntactic sugar for single non-dynamic dispatch, and encapsulation. On the other (but still very sweet) hand, is Ruby OOP when you write 10.times { puts "hello" }? I don't know. Or rather: it's completely arbitrary. Even what is the most useful, when used cleanly is not exclusive of "OOP": invariants are also most important, and arguably way more well known by practitioners, in FP.

Natural language is inherently descriptive. If there is a better "OOP", you just have to fight to make it prevail, so that people think of it when they hear "OOP". Maybe we can make the bad teachers stop in other ways too. Like not even using the word, and making "alternate" approaches (maybe even what you are calling OOP) fashionable.

Re:Pretending OOP Never Happened by AnimalMuppet on Friday May 15, 2020 @09:49PM (#23199740)
I think of OOP as encapsulation, inheritance, and polymorphism (but not necessarily all of them being used all of the time).

My problem with the example is that Book should not have a sell() method. Instead, BookStore should have a sell(Book) method. The machinery for how to send in a credit card transaction does not belong in Book. You still need it - it has to live somewhere - but BookStore is the place, not Book.

Re:Pretending OOP Never Happened by jholman on Saturday May 16, 2020 @07:06AM (#23202439)
> OOP as encapsulation, inheritance, and polymorphism

Ugh.

Polymorphism is far from unique to OO, and predates it.

If by encapsulation you mean information hiding, that's as old as the birth of OO. I've also sometimes encountered an alternate definition of encapsulation that more or less means "information hiding in OO", which seems a bit circular to me?

That doesn't mean OO can't have those features, but you didn't say "OOP usually includes loops and conditionals", because it's too obvious to mention.

Re:Pretending OOP Never Happened by dingoegret on Saturday May 16, 2020 @01:09AM (#23200660)
Exactly this. As someone who abandoned OOP, I find that my programming has improved a lot. But whenever I hear criticism of OO, it's always about this ancient taxonomic structuring (is-a has-a) idea that no one doing post 90s OOP is doing at all. And it's always proponents of "simpler" procedural paradigms that harp on about this. Weird.
Re:Pretending OOP Never Happened by throwaway894345 on Saturday May 16, 2020 @11:47AM (#23204371)
If no one does this taxonomic structuring or god object design, then what distinguishes good OOP from data oriented programming? Also, taxonomic design was popular in code and in pedagogy (in Java, C#, and C++ anyway) when I was in school circa 2010.
Re:Pretending OOP Never Happened by throwaway894345 on Friday May 15, 2020 @09:48PM (#23199734)
That’s pretty much my point. If you strip this away, what is left to distinguish it from other paradigms? Inheritance?
Re:Pretending OOP Never Happened by AnimalMuppet on Friday May 15, 2020 @10:47PM (#23200044)
See my reply to tenac. The problem isn't that OOP is bad, the problem is that you're doing it badly. It's not that you misunderstand what the ideas are, either. It's just... really bad design.

You even know what the problems are with the design - you described them quite well. But it's not showing that OOP is horribly flawed. It's just showing you that you need a different design.

Re:Pretending OOP Never Happened by throwaway894345 on Friday May 15, 2020 @11:56PM (#23200364)
I’m pretty convinced that it’s bad design, but many OOP practitioners (including textbooks and thought leaders for a good while) disagree. And even if you agree that all of these facets of bad design (including inheritance hierarchies) are Not True OO, then what is left that distinguishes object- from data-oriented design? What value does “OO” add to our lexicon?
Re:Pretending OOP Never Happened by AnimalMuppet on Saturday May 16, 2020 @04:58PM (#23207159)
I didn't say that inheritance hierarchies are bad. They're great... if you've got entities that are actually in a hierarchy. I've used a hierarchy in a genetic programming situation, with a Statement being the base class, and a number of derived classes that implemented the available instruction set, with a virtual execute() method. It worked great... for that problem.

If you're having a hierarchy just to have a hierarchy, that's about as wise as having gotos just to have gotos. Nobody sane would do that today; maybe we'll get there with hierarchy, too.

I'm not sure what your definition of "data-oriented design" is, so it's hard for me to say how OO is different. I'll take a stab at it anyway, but know in advance that my response may be orthogonal to your question.

You've got data - say, data about a book, in the original example. In the structured programming days, that would be an "entity"; now it's an "object". The difference is that, with private methods, nobody can modify the book's data just by having a reference or a pointer to it. (You could kind of do this in C with a source file that would operate on the structure, and some of the methods being file static. But that doesn't keep any other code that has a .h file from modifying the structure without using the functions.)

The philosophical difference with OO might be that OO uses the compiler to enforce that data is only accessed in approved ways. This is a logical extension of static typing. (Of course, for non-static-typed OO languages, it doesn't happen that way. They enforce it at runtime.)

Re:Pretending OOP Never Happened by throwaway894345 on Monday May 18, 2020 @02:21PM (#23226346)
Sounds like OO to you is all about encapsulation. I think that's fine, but lots of paradigms support encapsulation. It's absolutely pervasive in FP and DO programs, for example (even C supports opaque pointers). Do we say any language with encapsulation is also object-oriented?

Or is OO encapsulation + inheritance? One problem is that the OOP community seems split about whether or not inheritance is a defining feature of OOP. And since encapsulation isn't a defining feature, surely it's inheritance. Unfortunately, there's no clear consensus, so OO as a term is not very useful.

> The philosophical difference with OO might be that OO uses the compiler to enforce that data is only accessed in approved ways. This is a logical extension of static typing. (Of course, for non-static-typed OO languages, it doesn't happen that way. They enforce it at runtime.)

This is an interesting distinction; ironically C compilers support encapsulation via opaque pointers while Python doesn't enforce encapsulation even at runtime (it's all based on convention--"consenting adults" and all that).

> I didn't say that inheritance hierarchies are bad. They're great... if you've got entities that are actually in a hierarchy.

I think there are cases where inheritance doesn't overtly bite you, but I've never seen a case where inheritance is actually cleaner than composition (with the exception that in many languages, inheritance is the only way to automatically delegate--they lack something like Go's struct embedding). Specifically in your Statement example, you could have gotten your polymorphism from a Statement interface instead of a base class.

Inheritance seems like just a particular subset of composition and polymorphism--there are cases for which it is appropriate, but why does it need to exist at all when you can get the same benefits from composing the constituent components (components being polymorphism and composition via interfaces/first-class-functions and object-/functional-composition, respectively)? It feels like having an add4() function--it's not always bad (sometimes you really need to add 4 to another number), but the more general 2-argument add() function works just as well in the cases where add4() doesn't overtly bite you, and it's much less likely to be abused.

Re:Pretending OOP Never Happened by dingoegret on Saturday May 16, 2020 @01:15AM (#23200693)
The problem with your claims now is that they are unfalsifiable. How do we go about proving or rejecting your ostensible claim that this how OOP programmers say it should be done? The OOP community has a vibrant culture of critiquing bad designs. See DDD and similar efforts.
Re:Pretending OOP Never Happened by throwaway894345 on Saturday May 16, 2020 @11:45AM (#23204346)
> How do we go about proving or rejecting your ostensible claim that this how OOP programmers say it should be done?

Off the top of my head you could survey the landscape of OOP books, blog posts, and code bases throughout history. But it doesn’t really matter—even if you don’t believe me and all OOP programmers believe these are design flaws, then my question remains: what distinguishes “good OO design” from data oriented design?

Re:Pretending OOP Never Happened by SPBS on Friday May 15, 2020 @09:33PM (#23199632)
If Go's paradigm is considered OO then what would you consider non OO? Seems like almost every language has the baseline of struct and struct methods (with private and public semantics)
Re:Pretending OOP Never Happened by dragonwriter on Friday May 15, 2020 @09:38PM (#23199661)
> If Go's paradigm is considered OO then what would you consider non OO?

Classic, pre-object Pascal. C. Very little new in the last few decades, because OOP is a very influential paradigm.

Re:Pretending OOP Never Happened by hnick on Friday May 15, 2020 @10:04PM (#23199823)
Even though it's not OO that doesn't stop people adding their own hacked OO to C. I think the Quake source code had a lot of this, structs with function pointers. OO is such a natural fit for games in particular it finds a way to exist.
Re:Pretending OOP Never Happened by throwaway894345 on Friday May 15, 2020 @10:28PM (#23199952)
The whole GObject ecosystem is hacky object oriented C, to provide another suite of examples.
Re:Pretending OOP Never Happened by hnick on Friday May 15, 2020 @11:13PM (#23200168)
I personally found hacked OO ala Perl to be the first time I really understood it, even after years of class diagrams at university. The abstraction got in the way. I needed the nuts and bolts.
Re:Pretending OOP Never Happened by throwaway894345 on Saturday May 16, 2020 @11:51AM (#23204402)
Yeah, I delved into it when I was working with C, but by that time I had no interest in reproducing inheritance but only interfaces.