| This article, like many that cheer functional programming, falls into a certain cognitive bias, that prevents it from seeing what OO is good at. Alan Kay wrote "The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be." To start to see what this means, consider the annoying String / Data.Text split in Haskell. String is very much in the "leave data alone" mindset, baring its guts as a [Char]. Now you're stuck: you can't change its representation, you can't easily introduce Unicode, etc. This proved to be so rigid that an entirely new string type had to be introduced, and we're still dealing with the fallout. Great and growable systems! The large scale structure of our software, decomposed into modules, not just at a moment frozen in time, but in the future as well. We are tasked with thinking about relationships and communication. So here is how "the better way" is introduced: > Data is immutable, and any complex data structure... There's that insidious FP bias: to immediately dive into data structures, to view all programs as a self-contained data transformation. So reductionist! It's completely focused on those "internal properties and behaviors" that we were warned about above. I would end here, but I just couldn't pass this up: > To come up with a better solution [for dispatching], Haskell and Clojure take very different approaches, but both excel what any OO programmer is commonly used to. "Any OO programmer?" No way! OO as realized in truly dynamic languages exposes not just a fixed dispatch mechanism, but the machinery of dispatch itself, i.e. a metaobject protocol: "...there are not only classes for workaday tools, such as UI widgets, but there are also classes that represent the metastructure of the system itself. How are instances themselves made? What is a variable really? Can we easily install a very different notion of inheritance? Can we decide that prototypes are going to work better for us than classes, and move to a prototype-based design?" This is far richer than the anemic switch-style dispatch that Haskell's guards and pattern matching provide. For example, try modifying a Haskell program to print a log every time a string is made. You can't! I'm not familiar with Clojure but I'll bet its object model has its roots in CLOS. Whether or not you call it "object oriented," CLOS is solidly in the spirit of having a dynamic meta-structure. |
I avoid "traditional" OO in my own work for the some of the same reasons the author points out; not least of which that traditional classes are a kitchen sink.
But many of the ideas of OO; notably extensionality (what the author incorrectly calls intensionality), I could never do without. I agree with you, that exposing the innards of my data structures is a crime: not only do I lose control over their construction and use (including defining equality), but I'm restricted from ever modifying the structure.
But nothing in FP prevents hiding structure. You can see it all the time in OCaml: a module signature will declare an opaque, possibly parametric, type, as well as a set of operators over that type. The internal structure of that type is never exposed. All creation and use, and ideally comparisons (though it is unfortunately not enforced in OCaml) must go through the module's API.
(Module signatures, it should be noted, may be shared by multiple implementations, permitting compile-time dispatch.)
Yet while maintaining opacity, I am free to dispense with the excess baggage an OO class entails: run-time dispatch; a single "self" (i.e. the "friend" problem); that abomination known as inheritance; all these things I need no longer worry about, and my code can be cleaner and more efficient.