May be this is just in my head because I recently discovered this talk (ignore the incendiary title): https://www.youtube.com/watch?v=QM1iUe6IofM
May be this is just in my head because I recently discovered this talk (ignore the incendiary title): https://www.youtube.com/watch?v=QM1iUe6IofM
In OOP the line between data and verbs blur into a combination of the two called an object. The object is a primitive and if your primitive is a combination of data and function you cannot program things in a way that has data first. At least you can't do it without making the code awkward.
FP is not, despite the name, a verb first language. It separates verb and noun allowing you to choose to build a program in a way that is noun or data oriented while OOP does not.
Data is a noun that cannot do anything except exist. Functions are verbs that cannot do anything but change things. Objects are a combination that are neither and thus hard to do data oriented programming if objects in your code exist as primitive building blocks.
All this article is saying is make the data a primitive building block.
So data are models, functions views and objects controllers?
To respond to your point, though:
Traditional OOP wants to meld behavior into the noun. Which should make the data-first (noun-first?) POV more apparent -- data isn't first-class when it's coupled to behavior as traditional OOP usually has it. So functional-oriented program is more data oriented as data remains first-class (ie decoupled from behavior).
This ("objects-based" programming; or programming with "abstract types") actually works fine. The part where OOP leads to real problems that make it inimical to true modularity is all about the tacked-on features of inheritance and polymorphism; specifically, implementation inheritance. Because that means you've started relying on the very interface you were supposed to define in order to implement some other behaviors implied in it, and then for good measure you're allowing that interface to change in practically arbitrary ways as new "derived" classes are defined. It's not surprising that this fails to work well.
This is the POV of the OOP-ist, but it is not necessary and it is limiting. It's actually the "debate" we are having, so asserting it isn't proving it!
Functional programming has a complete story for polymorphism so OOP does not win on that account contrary to what you're implying.
I do think we have common ground in shunning class inheritance which is useless and a complete disaster.
However even without inheritance it seems to be that "objects" are provably replaceable by functions. (E.g., promote the implicit `this` reference to a first-class reference provided as an explicit arg to functions.)
I see no reason why data can't have opaque parts so data encapsulation doesn't seem to be a unique OOP claim either.
So then if objects aren't necessary for polymorphism and data encapsulation, then what are they good for?
How is it "limiting"? And if you want proof, look at the untyped lambda calculus - there you find data types defined entirely in terms of functions - pure behavior! (For example, the Church natural numbers are defined by the behavior of iterating some arbitrary function exactly n times; the Church booleans by taking two arguments and returning either the first or the second argument (which in turns makes it possible to define if-then-else, a sort of pattern matching); and so on and so forth.) It just so happens that this behavior-focused encoding is enough to express arbitrary programs - which is the opposite of limiting!
In every programming environment that I am aware of, such data-less functions you describe would be happily deleted without any worry to customers & stakeholders.
For example:
add : Int -> Int -> Int
This may look nice on paper but on a real computer there are bounds to this purity. And anyway my program only becomes useful when actual integers are instantiated and appearing on stacks and the heap.The "data-first" ideas we're discussing here ask one to stop obsessing over the functions and model the data soundly. You'll find any PL will do when operating over sound data expressions. This approach ime brings clarity and power to problem solving.
Theory divorced from practice is limiting. This is not a philistinic take, btw -- theory is supremely powerful when applied successfully for outcomes. But the "pure behavior!" you're talking about here seems too excitedly far away from practitioner-space.
I also don't imply it's "50/50" as I really don't know what works or doesn't, it could be data-types first design works in 85% of the cases and so it's good to promote it or think in terms of design that way. I just don't like "X is the right way" talk in general, although it doesn't seem like the author is proposing this be a hard fast rule.
However, having built systems in OO and then in functional, I've come to realize that the OO tools always have a surface of familiarity (`cat.meow()` vs `meow(cat)`) but are highly coupled (inflexible) while the latter, functional composition is not.
And if there's any objective measure to complexity it's a measure of degrees of coupling. And OO distinctly loses to functional there.
Maybe a different example would better illustrate the point?
"Even the simplest procedural logic is hard for humans to verify, but quite complex data structures are fairly easy to model and reason about. To see this, compare the expressiveness and explanatory power of a diagram of (say) a fifty-node pointer tree with a flowchart of a fifty-line program. . . .
"Data is more tractable than program logic. It follows that where you see a choice between complexity in data structures and complexity in code, choose the former. More: in evolving a design, you should actively seek ways to shift complexity from code to data."
--- http://www.catb.org/~esr/writings/taoup/html/ch01s06.html
To paraphrase with a modern example, imagine a thousand lines of JSON, with deep nesting and all kinds of fields. It would still be easy to understand. Even a nonprogrammer could read it and find the part he's looking for (say, someone's address).
Compare that to a thousand lines of procedural C code, or even a thousand lines of Python. That would be much harder, and your nonprogrammer friend would throw up his hands and walk away.
The thing you hopefully learn after years of programming is that you can trade the length of one for the other. That is to say, you could simplify the JSON into half its length, if you make the code that processes it more complicated. Or you could simplify your procedural code by storing more complexity in your JSON data structure. The advice is to do the second thing, "to shift complexity from code to data."
Also, from another chapter:
"Data-driven programming is sometimes confused with object orientation, another style in which data organization is supposed to be central. There are at least two differences. One is that in data-driven programming, the data is not merely the state of some object, but actually defines the control flow of the program. Where the primary concern in OO is encapsulation, the primary concern in data-driven programming is writing as little fixed code as possible. Unix has a stronger tradition of data-driven programming than of OO."
--- http://www.catb.org/~esr/writings/taoup/html/ch09s01.html
What is the difference between a type and a class though? Classes have modes of interaction through their interfaces, types can only be interacted with using specific operators. To me they are two sides of the same exact coin.
I think the simplification of OOP as "noums first" loses too much. That "first" there isn't chronological, but mere visual weight.
There are patterns of OOP design that put data first, but there are also way too many that put behaviors first, but that behavior is owned by some data. At the other way around, people doing "verbs first" programing are even more likely to start with their data design than the OOP ones, because they lack of some boilerplate structure and need to get something to fundament their design.