Hacker News new | past | comments | ask | show | jobs | submit login

If you eliminate polymorphism and inheritance, is what's left really all that useful?



The one we can dispense with is encapsulation.

Polymorphism, no; I mean, how can we substitute different kinds of objects into the same situations dynamically without polymorphism. OOP is almost synonymous with polymorphism.

But we do not have to bundle code and data representation together; there are other approaches to OOP, like generic functions.


I don't think we can dispense with encapsulation and still have OO, though. Sure you can program without it. You can even make good programs without it. You may even be able to make better programs without it :-) However, it won't be OO.

The idea behind OO is to move away from data-centric programming. If I have a system dealing with geometry, I can have a Point object that has an x and y coordinate. However, once I have a Point, I should be trying to make my system so that I no longer access x and y directly. X and y are "encapsulated" in the Point. Collaborators should use the functionality on Point to do what they need to do, rather than trying to pry Point apart and get at the data.

Of course, barely anybody writes "OO" code like that, even if they espouse to follow "encapsulation".

I would argue as well that generic functions do not, in themselves offer an OOP environment. You need one more thing: partially applied functions. If you can create a partially applied function, you can bake in some program state and pass it around, offering functionality on that program state. However, nobody on the outside can access that state. This is the encapsulation necessary for OO.


Point = {x, y} is a strange example. What exactly is being encapsulated? Any Point structure I've ever seen has accessors for x and y, and no other internal data.

In general, I don't see the need for encapsulation in order to qualify for object status. The classic counterexample is CLOS, which doesn't have it -- and the debate over that has been raging for decades [1].

[1]: http://wiki.c2.com/?HowObjectOrientedIsClos

The best basis for the essence of object-orientedness that I ever read was suggested in an article that focused on identity. (I don't remember where I saw this -- possibly from Kent Pitman?) If you could guarantee that some value is exactly the same as a value that you had earlier, and not just a mostly-copy but identical in every respect, then you had an object. Everything else falls out of that.


This is a great question. I thought that myself, so I tried to make a system where I couldn't access x and y. It turns out it's completely reasonable to do so. If you want to move a point, you add a translate method. If you want to compare points you can. You can add and subtract points. If you want to align a group of points on the X access, you can write a method on point to do it. There were very few times when I wanted to actually access x and y rather than act on it.

I do agree, though, nobody actually writes code like that. Does that mean that I think nobody actually does OO? I hadn't actually thought it through that far :-) I think this is why the concept is so vague (your excellent example being a great indicator of that). C++ is often referred to as "C with Objects" and I sometimes think we've gone so far as to say anything with a function that operates on a structure is "Object Oriented". If so, I think we've lost our path a little bit.


You can keep adding mathematical transformations to the Point object all day long (though at some point you'll realize the mathematical operations don't belong there, and the concept of "point" isn't abstract enough - the study of right abstractions here is called "algebra"). But there comes a moment when you want to draw that point on the screen, and suddenly there's a good chance you'll have to access individual components, because the API of your drawing device doesn't understand your particular Point class.

Arguably this is still within purview of "Point" abstraction, but:

> If you want to align a group of points on the X access, you can write a method on point to do it.

This calls for a static method really, otherwise the abstraction becomes subtly wrong. And with static methods you quickly realize that you're using the containing class as just a namespace.

CLOS actually resolves this nicely by having methods being their own things separate from classes. As a related side effect, the typical C++/Java case of "object methods" isn't a special thing in CLOS, it's just a method that happens to be polymorphic only on its first argument. The abstraction around methods seems cleaner, and the class in CLOS is just responsible for encapsulation and inheritance, not for namespacing methods.


It sounds like all you're really doing is taking any function that does want access to X and Y, and shoving it into the Point object where it has that access. Or to put it another way, you're defining any function that wants access as "part of the concept of point". You can indeed accommodate any code with this transform, but is it the right thing to do? Do you gain anything by bloating up 'point' with transformations and comparisons and alignment operators and so on?


Of course you gain something. By encapsulating all methods that operate on X and Y in a single place, Point (where X and Y are defined), it is now _much_ easier to change X and Y. It's all right there in one file.

Point {x, y} may not be the best example, but you can certainly see how this can be beneficial should X or Y become something even slightly more complex. And this is the purpose of encapsulation - to make a system easier to understand and therefore easier to change.


The Point could internally be represented in a polar coordinate system.

I kind of agree that encapsulation isn't needed for object status. The caveat only being that it would be a weird object system where you could be sure there was no encapsulation.

I mean, OK, in some languages you know that. But does it change anything important?

https://en.wikipedia.org/wiki/Polar_coordinate_system


Even if a point uses polar coordinates internally, we can have syntax "p.x" which gets the Cartesian x.

Information hiding is an important concept in low-level-ish systems programming languages in which p.x implies a piece of machine code that offsets 24 bytes from the base address of p, and fetches a four byte integer.


This Point class is a poor example because it does not have anything that is not part of its interface, and, as presented here, does not have any constraints on it.

The intended purpose of encapsulation is to try and preserve integrity of a system by restricting the ways a programmer might put it into an inconsistent state. Knowing that certain constraints are in place simplifies the problem of understanding and debugging a system.


Python is build entirely around the idea that encapsulation is mostly useless. "We're all consenting adults" is the general philosophy. As long as there's a convention as to what properties are "public" and which are not, a motivated user of a class should be able to do what they want with it.

There's no point in putting a "private/public/protected" security model in a programming language. It isn't a security boundary.

This vastly simplified the design of OO in Python, and it makes testing far far easier. There's no need to go through the dependency injection acrobatics in order to change one method for testing or proxying.

(There technically is a way to munge a property name in Python but its use is discouraged).


I wish there was a "do not touch unless you know what you're doing" field. Private fields and members are a nuisance if you need to do some unsupported thing with a library. On the other hand, there are often utility functions in an object that are absolutely only ever meant to be used by that object itself, and not to be called from the outside.


Python supports those by prefacing with "__", IIRC.


There's no point in putting a "private/public/protected" security model in a programming language. It isn't a security boundary.

It’s about intention. If I mark a property or method as private, it means that I can get rid of it, rename it or change the expected behavior without considering it a breaking chance.


Yeah, and Python has some standard nomenclature for marking a property as protected and a little bit of behavior for helping mark it as private. You do not need encapsulation for that.


And there is nothing about the Pythom runtime stopping someone from using it anyway.

With a statically typed language, you really have to go out of your way to do it like using reflection and if your code breaks on an update you get what you deserve.


YAGNI rules the day.


You’ve never had to refactor code that was used as a library by other developers?

Do you realize how many hacks is built into Windows just because developers were using undocumented features?


> (There technically is a way to munge a property name in Python but its use is discouraged).

Do you have a reference for that (i.e. the discouraged part)?


Note that "encapsulation" refers to two completely different things, one of them by mistaken application of the word:

- combination of methods and data

- information hiding

Information hiding is needed for OOP, but it can be hidden in the implementation. The fact that if I have a point object, I can access point.x is not an example of information hiding being violated. Firstly, it doesn't interfere with polymorphism: the same point.x code can access the x coordinate of points of different types. Secondly, the syntax point.x can actually be a method call which invokes an accessor under the hood, so the object doesn't actually have to have a slot x. There is no semantics or "moral" difference between having a pair of methods point.getx() and point.setx(<number>), and just a point.x. Lastly, sometimes it is perfectly acceptable in an object design to have trivial properties.

Information hiding is more important in languages like C++ because an access like "point.x" compiles to code which assumes that x is at a particular offset in that object, and is of a particular type and so forth. It's less important in a dynamic language in which we can cafely ask for the x slot of any object.

Encapsulation of methods and data isn't required in an OOP system; OOP systems like CLOS work without it just fine.


I'm pretty sure Perl's Native OO has no encapsulation, at least without custom modules or weird wranglings like inside-out objects. It doesn't mean we must peek inside, but the option is there. It is completely functional, but as prone to misuse as the rest of Perl.


One should note that in Perl 6, object encapsulation is complete. You can only directly access attributes inside a class, and only through (usually autogenerated) methods outside of it.


"The one we can dispense with is encapsulation."

To me this is the most important part. I like to have private or public, protected is not needed.

I agree that code representation should be kept separate.


> To me this is the most important part. I like to have private or public, protected is not needed.

OO's encapsulation is about bundling data and operation, not about information hiding, and as kazinator noted generic functions (as in CLOS) prove you can have OO just fine without.

As to information hiding, there are many fine OO languages which don't have it either (Python probably being the prime example there, but I'd say it also applies to languages like Ruby where you can trivially override existing ACL) so it's clearly not necessary either, let alone "the most important part". Especially as conversely many non-OO languages do have access control, hinting that the two are quite likely orthogonal.

Information hiding is useful for engineering and resilience, not for object orientation.

As to private / protected / public, I'd argue it's private which is the least useful (since we've already ascertained none is actually needed): it's literally just hiding stuff from yourself. I think removing that level of ACLs is one of the few good idea Go had.


ACLs also seem to mix concerns. The "private" concept is essentially making life difficult for yourself, but having a built-in mechanism for separating public interface from internal code, that's a bit more than just convention, is useful. For instance, in Common Lisp there's no language-enforced ACL, but when building modules (that may or may not involve a class, or many classes), I assign a package for each module and make it export only the symbols that represent its public interface. In code using that module, this becomes a syntactic difference in a way you refer to these symbols - module:foo means "foo exported from package 'module'", while module::foo means "foo internal to package module". That simple isolation, completely overridable with an extra colon character, is enough to clearly communicate what is and what isn't a part of an abstraction.


Is there a warning when someone overrides that isolation? In C# you can also access private members through reflection and I have seen people use that a lot instead of bothering to understand why the original developer decided to make the variable private.


No, there isn't, because "we're all adults here". But you have to type :: instead of : to do this, which usually tells you something, and you don't have to go through reflection to do this, so such overrides don't cause performance issues. Common Lisp doesn't prevent you from accessing anything.


I wish we he had more adults instead of people fresh from college who think that best practices and things to avoid are obsolete and don’t apply to them :)


That's the unfortunate nature of exponential growth. If the number of programmers doubles, say, every 3 years, that means at any point in time, half of the workforce has less than 3 years of experience.


I mean, you can do pure functions. And those pure functions can operate on structs. And, once you're there, it's probably good hygiene to keep all the functions operating on a given struct in one file.

Congrats, you're back to 'encapsulation'! Encapsulation is actually the only important tool in that particular toolkit. We're mortals, we need coping tools to keep programs manageable.


That kind of "encapsulation" is simply storing code together with the data it belongs to (good practice) and possibly namespacing it together. It's not what they mean by "encapsulation" in OO courses, though it's arguably the only benefits "encapsulation" give you.


Would you be interested in doing a podcast together about programming? I'm a radical javascript programmer (search ONNEMI-4211). You seem smart. I've never made a podcast before.


On the one hand, you've got some amazing satire here, on the other hand, I'm a little insulted that it's directed at me of all people.

Upvoted anyways :)


Satire?


I like to use classes almost exclusively as a way to couple an object with methods directly related to it. Basically structs with methods. Useful enough for me.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: