In the VIEWPOINT section of Communications of The ACM
If you're interested in practical programming, the proceedings of ACM and the likes is the last source you want to read. These are often [pseudo]-scientific publications with no application in the real world. These are quite often unorthodox opinions made for publicity, for the writer to differentiate himself from the crowd and promote his own person.
I claim that the use of OOP is not as prevalent as most people believe, that it is not as successful as its proponents claim, and, therefore, that its central place in the CS curriculum is not justified.
I tend to disagree with your point. OOP is widely spread and works just fine. By the number OOP-based projects have likely surpassed developments done with other strategies (let's speak about modern time, 15-20 years).
However OOP is not a silver bullet. It works for some developments, doesn't work for others. Just like any other approach.
But one thing I need to mention is that a curriculum should communicate knowledge of different approaches. If it's OOP-based, it's wrong. If it's FP-based, it's wrong. It should cover them all or don't touch this topic altogether.
P.S. Why care about what is dominant and what is not? Just take what is suitable for the project at hand and leave the numbers to "researchers".
Can we really use immutability in OOP without losing all key OOP features?
Don't see why not. Been doing it for years before Java 8 got all functional anyway. Ever heard of Strings? Nice and immutable since the start.
- I start needing persistent (in the functional sense) data structures like lists, maps etc.
Been needing those all along as well. Invalidating my iterators because you mutated the collection while I was reading it is just rude.
- It is extremely inconvenient to work with cross references (e.g. tree node referencing its children while children referencing their parents) which makes me not use cross references at all, which again makes my data structures and code more functional.
Circular references are a special kind of hell. Immutability wont save you from it.
- Inheritance stops to make any sense and I start to use composition instead.
Well here I'm with you but don't see what it has to do with immutability. The reason I like composition isn't because I love the dynamic strategy pattern, it's because it lets me change my level of abstraction.
- The whole basic ideas of OOP like encapsulation start to fall apart and my objects start to look like functions.
I shudder to think what your idea of "OOP like encapsulation" is. If it involves getters and setters then just please stop calling that encapsulation because it's not. It never was. It's manual Aspect Oriented Programming. A chance to validate and a place to put a breakpoint is nice but it's not encapsulation. Encapsulation is preserving my right to not know or care what's going on inside.
Your objects are supposed to look like functions. They're bags of functions. They're bags of functions that move together and redefine themselves together.
Functional programming is en vogue at the moment and people are shedding some misconceptions about OOP. Don't let that confuse you into believing this is the end of OOP. Functional and OOP can live together quite nicely.
Really that it. Dykstra told us goto
was harmful so we got formal about it and created structured programming. Just like that, these two paradigms are about finding ways to get things done while avoiding the pitfalls that come from doing these troublesome things casually.
Let me show you something:
fn(x)
That is a function. It's actually a continuum of functions:
f1(x)
f2(x)
...
fn(x)
Guess how we express that in OOP languages?
n.f(x)
That little n
there chooses what implementation of f
is used AND it decides what some of the constants used in that function are (which frankly means the same thing). For example:
f1(x) = x + 1
f2(x) = x + 2
That is the same thing closures provide. Where closures refer to their enclosing scope, object methods refer to their instance state. Objects can do closures one better. A closure is a single function returned from another function. A constructor returns a reference to a whole bag of functions:
g1(x) = x2 + 1
g2(x) = x2 + 2
Yep you guessed it:
n.g(x)
f and g are functions that change together and move around together. So we stick them in the same bag. This is what an object really is. Holding n
constant (immutable) just means it's easier to predict what these will do when you call them.
Now that's just the structure. The way I think about OOP is a bunch of little things that talk to other little things. Hopefully to a small select group of little things. When I code I imagine myself AS the object. I look at things from the point of view of the object. And I try to be lazy so I don't over work the object. I take in simple messages, work on them a little, and send out simple messages to only my best friends. When I'm done with that object I hop into another one and look at things from it's perspective.
Class Responsibility Cards were the first to teach me to think that way. Man I was confused about them back then but damn if they aren't still relevant today.
Let's have a ChessBoard as an immutable collection of immutable chess pieces (extending abstract class Piece). From the OOP point of view, a piece is responsible for generating valid moves from its position on the board. But to generate the moves the piece needs a reference to its board while board needs to have reference to its pieces.
Arg! Again with the needless circular references.
How about: A ChessBoardDataStructure
turns x y cords into piece references. Those pieces have a method that takes x, y and a particular ChessBoardDataStructure
and turns it into a collection of brand spanking new ChessBoardDataStructure
s. Then shoves that into something that can pick the best move. Now ChessBoardDataStructure
can be immutable and so can the pieces. In this way you only ever have one white pawn in memory. There are just several references to it in just the right x y locations. Object oriented, functional, and immutable.
Wait, didn't we talk about chess already?
Best Answer
Java's garbage collector doesn't rely on reference counting techniques. Circular references do not cause any kind of problem in Java. Time spent eliminating perfectly natural circular references in Java is time wasted.
Not necessary. If you just compile all the source files at once (e.g.,
javac *.java
), the compiler will resolve all forward references without problems.Yes. Application classes are expected to be interdependent. Compiling all Java source files that belong to the same package at once isn't a clever hack, it's precisely the way Java is supposed to work.