... and is probably one of those articles that OOP was based on.
Not really, but it did added to the discussion, specially to practitioners that, at the time, were trained to decompose systems using the first criteria he describes in the paper.
First I want to know if my assessment is correct. Does the FP paradigm and this article philosophically disagree?
No. Moreover, to my eyes, your description of what an FP program looks like is not different from any other that uses procedures or functions:
Data gets passed from function to function, each function being intimately aware of the data and "changing it" along the way.
...except for the "intimacy" part, since you can (and often do) have functions operating on abstract data, precisely to avoid the intimacy. Thus, you do have some control over that "intimacy" and you can regulate it however you like, by setting up interfaces (ie. functions) for what you want to hide.
So, I see no reason why we wouldn't be able to follow Parnas criteria of information hiding using functional programming and end up with an implementation of a KWIC index with similar pointed benefits as his second implementation.
Assuming they agree, I'd like to know what FPs implementation of data hiding is. It's obvious to see this in OOP. You can have a private field that nobody outside the class can access. There's no obvious analogy of this to me in FP.
As far as data is of concern, you can elaborate data abstractions and data type abstractions using FP. Any of these hide concrete structures and manipulations of these concrete structures using functions as abstractions.
There is a growing number of assertions here stating that "hiding data" in the context of FP is not so useful (or OOP-ish (?)). So, let me stamp here a very simple and clear example from SICP:
Suppose your system needs to work with rational numbers. One way you might want to represent them is as a pair or a list of two integers: the numerator and denominator. Thus:
(define my-rat (cons 1 2)) ; here is my 1/2
If you ignore data abstraction, most likely you will get the numerator and denominator using
(... (car my-rat)) ; do something with the numerator
Following this approach, all parts of the system that manipulate rational numbers will know that a rational number is a
cons -- they will
cons numbers to create rationals and extract them using list operators.
One problem you may face is when you need to have a reduced form of the rational numbers -- changes will be required across the entire system. Also, if you decide to reduce at creation time, you might find later that reducing when accessing one of the rational terms is better, yielding another full scale change.
Another problem is if, hypothetically, an alternative representation for them is preferred and you decide to abandon the
cons representation -- full scale change again.
Any sane effort in dealing with these situations will likely start to hide the representation of rationals behind interfaces. At the end, you might end up with something like this:
(make-rat <n> <d>) returns the rational number whose numerator is the integer and whose denominator is the integer .
(numer <x>) returns the numerator of the rational number .
(denom <x>) returns the denominator of the rational number .
and the system will no longer (and should no longer) know of what rationals are made of. This is because
cdr are not intrinsic to rationals, but
denom are. Of course, this could easily be an FP system. So, "data hiding" (in this case, better known as data abstraction, or the effort to encapsulate representations and concrete structures) comes as a relevant concept and a technique widely used and explored, whether in the context of OO, functional programming or whatever.
And the point is...though one may try to make distinctions between what "kind of hiding" or encapsulation they are doing (whether they are hiding a design decision, or data structures or algorithms -- in the case of procedural abstractions), all of them have the same theme: they are motivated by one or more points Parnas made explicit. That is:
- Changeability: whether required changes can be made locally or are spreaded through the system.
- Independent Development: to what degree two parts of the system can be developed in parallel.
- Comprehensibility: how much of the system is required to be known to understand one of its parts.
The example above was taken from SICP book so, for the full discussion and presentation of this concepts in the book, I highly recommend checking out chapter 2. I also recommend getting familiar with Abstract Data Types in the context of FP, which brings other issues to the table.