One important thing to point out: functional programming and object-oriented programming aren't necessarily mutually exclusive. Scala, OCaml, and a lot of Lisps have proven that. Whether it's a good idea is another question entirely, but the point is that it can be done.
They're not mutually exclusive, but they tend to push program design in different directions. There's tension when you try to make a whole program OO and functional, but you can make pockets of OO when it really is the best approach, and give them a functional interface,
> One important thing to point out: functional programming and object-oriented programming aren't necessarily mutually exclusive.
I'm not sure that's true.
The essence of functional programming is treating a program as evaluating a function without carrying around state, a declarative approach that promotes things like pure functions and referential transparency.
The essence of object-oriented programming is passing messages between objects with identity, state and behaviour, an imperative approach and one that fundamentally relies on late binding at run-time.
Aren't these styles mutually exclusive?
Of course, you can have tools representative of both styles, such as higher order functions and abstract data types, in the same language; the languages you mentioned all do. But whatever merit the resulting hybrid programming style may have, I'm not sure that style is properly called either functional or object-oriented programming.
"The essence of functional programming is treating a program as evaluating a function without carrying around state"
Isn't a closure a function that carries around state? There's an old tale about this:
"The venerable master Qc Na was walking with his student, Anton. Hoping to
prompt the master into a discussion, Anton said "Master, I have heard that
objects are a very good thing - is this true?" Qc Na looked pityingly at
his student and replied, "Foolish pupil - objects are merely a poor man's
closures."
Chastised, Anton took his leave from his master and returned to his cell,
intent on studying closures. He carefully read the entire "Lambda: The
Ultimate..." series of papers and its cousins, and implemented a small
Scheme interpreter with a closure-based object system. He learned much, and
looked forward to informing his master of his progress.
On his next walk with Qc Na, Anton attempted to impress his master by
saying "Master, I have diligently studied the matter, and now understand
that objects are truly a poor man's closures." Qc Na responded by hitting
Anton with his stick, saying "When will you learn? Closures are a poor man's
object." At that moment, Anton became enlightened."
> Isn't a closure a function that carries around state?
Well, that's the question, isn't it?
In our present context, to me "state" suggests something persistent and independent, something that could be read and possibly written by multiple paths.
Do you view creating a closure as customising an existing function with specific data, or as defining a whole new function? I think of it as the latter, and it just happens to be at run-time. The resulting function is still pure, and any customisation based on particular data is an integral part of it that isn't externally visible, which doesn't feel "stateful".
There are so many facets in object-orientedness and you can ignore many of them and still have something that's clearly object-oriented.
For example, object-oriented doesn't mean you have to store all your state in objects and that objects must thus be mutable. You can, for example, return new objects of the same kind with new, modified state.
If you have to do your own memory management the first thing people reach for is reference counting. Reference counting means maintaining state and directly contradicts immutable objects.
So immutable objects only really work if you've got some kind of garbage collection available.
Maintaining mutable metadata for objects doesn't preclude having immutable objects. The object's value doesn't change when someone wants to inc/dec its refcount.
The strategy discussed involved lots of creating new objects that were the same as an old object but with one more piece of data. If you're using a reference counting implementation then every one of those methods needs to know about, and adjust, all of the reference counts that the object points to. This kind of busy accounting work is error-prone, and does a lot to undo the simplicity that you're trying to achieve with this methodology.
Yes, I know there are differences, but many of the principles are similar. I decided not to write a treatise on GC ^_~.
So this is more of a internal vs. external state thing. Refcounting breaks the purity of the interface and requires you to think about memory management. Sure, I can see why you'd find that unacceptable and would break this model of programming. But I'm a C guy, so that doesn't really bother me, I guess ^_^.