Hacker News new | ask | show | jobs
by ravenstine 3459 days ago
There seems to be a common fixation on objects "modeling the real world" with the counter argument for classes being that they don't model the real world. True, there isn't a physical chair class in real life(philosophically, there aren't even chairs), but there is a common abstract idea of what would describe the function and attributes of a chair. Granted, it differs a little from person to person, but objects themselves are all different in some way. As far as I can tell, in most OO languages, there's no reason why classes must use inheritance, though I am certainly willing to be schooled in that regard.
5 comments

Plato would argue that there is such a thing as a chair. In fact, he'd argue there is an ideal of a chair which all chairs strive to achieve. The idea of Platonic Form (and the allegory of the cave) are very relevant to programming because western philosophy has shaped how we view the world, and thus how we program. That being said, I don't think most people I know are familiar with platonic forms, or the allegory of the cave, so perhaps a programming language based around that idea isn't very strong.

In my experience, the vast majority of people who program are doing so not as their full-time job, but out of necessity. This includes scientists, analysts, academics, accountants, etc. If I had to guess, these individuals write way more code as a whole than full-time developers. Adherence to the ideals object-oriented programming is extremely uncommon from what I've seen. I see a lot of copy-and-pasted code in one gigantic class, and a mish-mash of programming styles from wherever they stole their code from. Testing is completely unknown to them.

I think a lot of these considered harmful discussions are carried along by the impact GOTO Considered Harmful had on the programming world, but I don't think they actually address concerns that affect the vast majority of people writing code. They are of obvious interest to Hacker News readers because people here are trying to maximize their productivity and are extremely tech savvy already, but if you are trying to write a language which minimizes the impact that mistakes make on the world I think you should look at where the vast majority of code is being written. The biggest players already have the knowledge to program effectively, it's about creating ways of coding that make good programming more intuitive, and I don't think OO is the problem there.

Thank you very much for mentioning Plato here. I was thinking exactly about this wile reading this paragraph:

> Let us also be clear that classes do not “model the real world”. Objects may or may not model the real world, but classes certainly don’t. Although there are many chairs in the real world, there is no “chair class” in the real world.

> As far as I can tell, in most OO languages, there's no reason why classes must use inheritance, though I am certainly willing to be schooled in that regard.

That there is no precise definition of what a class is and isn't, and that the traditional notion of a class conflates several independent concepts, is precisely the big criticism! To my mind "traditional classes" means inheritance, and recent languages without inheritance (Rust or Go) have tended to not use the term "class".

Rust actually uses algebraic data types. This allows for relatively easy and reliable type inference.

But ADTs do not support inheritance, so Rust uses Traits instead (comparable to interfaces or typeclasses), that support inheritance of sorts, but without the usual perils of class-based inheritance, like the diamond problem.

Forgive my ignorance. What do Rust and Go call their data structures that are analogous to classes?
There really isn't a direct analog in Rust. The best approximation in Rust is actually a combination of constructs in the language: structs and traits.

Structs are almost exactly like the C-objects of the same name: they define layout in memory for a data structure with discrete types as fields, and there are various packing options available. However in addition to this a struct-specific implementation can exist which defines static and instance based methods. As there is no inheritance in Rust, a struct's "impl" is unique to itself.

Polymorphism is implemented via the traits system. A trait defines a set of methods that objects meant to have said trait must implement (unless a default implementation for a given method in the trait exists). Any struct can have an implementation for a given trait, so these can be considered a (rough) analog to abstract base classes/interfaces (e.g. C++ classes that are either pure virtual or have virtual functions with default implementations).

You say traits are a rough analogue for C++ base classes with virtual methods. What are the fundamental differences?
For one, traits are inherently static and their methods are statically dispatched, somewhat like a concept. Traits which satisfy certain conditions which make them "base class-like" can be reified as a trait object, which is a vtable ptr + data ptr pair, which dynamically dispatches through the vtable. The separation of the vtable ptr from the object means every trait object gives you dynamic dispatch for one trait only and you can have an unbounded number of trait objects, in contrast to C++ where you have dynamic dispatch through the finite number of base classes listed by the class author.
I'm not familiar enough with the internals of how Rust handles v-tables and the like in light of its other features to answer that competently.

In practice, one defines an interface in C++ by having a (hopefully) stateless class with pure virtual method declarations, and then classes derived from this class must implement these methods (in order to instantiate them anyway). In Rust one defines a data structure (either a struct or enum) and then, separately, writes the "impl" for it for a trait.

The big difference is that when you have a trait object in Rust, it's a double pointer: a pointer to the vtable, and a pointer to the data. In C++, in my understanding, you'd have a single pointer to both the vtable and data laid out next to each other.
The whole point is to unbundle what classes are, so there is no direct analogy. Structs are one piece of traditional classes; traits (rust) or interfaces (go) are another part, and delegation a third part.
Well, in Go they are just Types
Well I'm a lover of scala and I dislike deep inheritence.

That's actually why I love the model of golang and rust. They model the real world more closely.

When you look at a desk, how do you define it?

Well if it's a wooden desk it's actually the type Wood and it porbably has 4 table legs and probably a desk size of x*x. So this is way easier to represent with Subtyping, since a Desk can be made of wooden, but doesn't need to, so with structual inheritance that would mean a lot of boilerplate if you would need to define all type's of desks while in rust you would just add the impl trait to any of the base struct's, etc.

Also deep inheritance can be nasty to debug, even Scala will probably move to a more flat level in their collections library.

Right, classes are a way to explain to the computer 'there's gonna be a bunch of objects that are all "chairs". You'll be able to treat all of them in a similar kind of way.' But you should consider that starting with classes might not be the only way to do that.

Inform7, in its beautifully literal (not to say literate) way, captures this very simply with the way it defines classes (what it calls 'kinds'):

     A chair is a kind of thing. A chair can be comfy or hard.
     Your favorite armchair is a comfy chair in the living room.
But to be fair, Inform7 is object-oriented because it's a language for describing worlds (interactive fictional worlds, specifically) that are made of objects, and the utility of the metaphor for building, say, a website, may not be as strong.

Other languages might use other techniques to capture 'chairness' without having to start off by describing the class of 'chair'. Maybe you define the protocols chairs support (sittability?), or you just rely on dynamic typing and write code that just assumes whatever is passed to it is a chair and can be sat upon, without the code ever needing to be able to divine an object's chairness. That's useful! you can sit on things that aren't chairs, after all. If you can only build objects from classes, though, you can get yourself into the situation where you find yourself having to find a way to compose a 'chair' instance into your 'bed' class so you can allow someone to sit on the bed without having to copy paste code.

>There seems to be a common fixation on objects "modeling the real world" with the counter argument for classes being that they don't model the real world.

I'm not sure why this is the case. Just the other day someone posted a link to a video about Abstract Algebra. In the video they defined "groups" that (to me) are synonymous with Classes in OOP.

My first exposure to abstract algebra is ultimately what made me become a programmer. I had dabbled with it before, and even done the first year papers, but none of it really stuck with me. After reading about algebraic structures it all started to make sense.