27

There's a classic article named On the Criteria To Be Used in Decomposing Systems into Modules that I just read for the first time. It makes perfect sense to me, and is probably one of those articles that OOP was based on. Its conclusion:

We have tried to demonstrate by these examples that it is almost always incorrect to begin the decomposition of a system into modules on the basis of a flowchart. ... Each module is then designed to hide such a decision from the others

In my uneducated and inexperienced opinion, functional programming takes the exact opposite advice of this article. My understanding is functional programming makes data flow idiomatic. Data gets passed from function to function, each function being intimately aware of the data and "changing it" along the way. And I think I've seen a Rich Hickey talk where he talks about how data hiding is overrated or unnecessary or something, but I can't remember for sure.

  1. First I want to know if my assessment is correct. Does the FP paradigm and this article philosophically disagree?
  2. Assuming they disagree, how does FP "compensate" for its lack of data hiding? Perhaps they sacrifice data hiding but gain X, Y and Z. I'd like to know the reasoning for why X, Y and Z are considered more beneficial than data hiding.
  3. Or, assuming they disagree, perhaps FP feels that data hiding is bad. If so, why does it think data hiding is bad?
  4. 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.
  5. I feel there are other questions I should be asking but I don't know I should be asking. Feel free to answer those, too.

Update

I found this Neal Ford talk that has a very relevant slide in it. I'll embed the screenshot here:

enter image description here

8
  • 1
    I can't answer the full question, but as for (4), there are module systems in some FP languages which can provide encapsulation.
    – Andres F.
    Oct 4, 2013 at 22:35
  • @AndresF. ah yeah that's true. I forgot that Haskell has modules and you can hide data types and functions in them. Perhaps when I say FP I'm really saying Clojure. You can have private functions and "fields" in Clojure, but I feel like it's idiomatic to make your data visible to anything and pass it anywhere. Oct 4, 2013 at 22:42
  • 6
    What you often do is make your types visible, but hide your constructors. These abstract types are done particularly well by the OCaml module system Oct 5, 2013 at 8:37
  • 7
    In a ML-like language, not having access to the constructors means that you can't pattern match over a value of that type to deconstruct it. The only things you can do with those values is to pass it to whatever function was made available. It's the same sort of data abstraction as is done in, say, C which doesn't have first-class notions of what is public or private either.
    – Luc Danton
    Oct 5, 2013 at 9:32
  • 1
    @SK-logic: From an "expression problem" point of view, revealing the data is good when you want to extend with new function in the future (and are OK with keeping the data fixed) and hiding the data is good when you want to extend with new datatypes in the future (at the cost of keeping the functional interface fixed)
    – hugomg
    Oct 5, 2013 at 23:36

7 Answers 7

22

The article you mention is about modularity in general, and it would apply equally to structured, functional, and object-oriented programs. I have heard of that article before from someone who was a big OOP guy, but I read it as an article about programming in general, not something OOP specific. There is a famous article about functional programming, Why Functional Programming Matters, and the first sentence of the conclusion states "In this paper, we’ve argued that modularity is the key to successful programming." So the answer to (1) is no.

Well designed functions don't assume more about their data than they need to, so the part about "intimately aware of the data" is wrong. (Or at least as wrong as it would be of OOP. You can't program strictly at a high level of abstraction and ignore all details forever in any paradigm. In the end, some part of the program does actually need to know about the specific details of the data.)

Data hiding is an OOP specific term, and it isn't exactly the same as the information hiding discussed in the article. Information hiding in the article is about design decisions that were hard to make or are likely to change. Not every design decision about a data format is hard or likely to change, and not every decision that is hard or likely to change is about a data format. Personally, I can't see why OO programmers want everything to be an object. Sometimes, a simple data structure is all you need.

Edit: I found a relevant quote from an interview with Rich Hickey.

Fogus: Following that idea—some people are surprised by the fact that Clojure does not engage in data-hiding encapsulation on its types. Why did you decide to forgo data-hiding?

Hickey: Let’s be clear that Clojure strongly emphasizes programming to abstractions. At some point though, someone is going to need to have access to the data. And if you have a notion of “private”, you need corresponding notions of privilege and trust. And that adds a whole ton of complexity and little value, creates rigidity in a system, and often forces things to live in places they shouldn’t. This is in addition to the other losing that occurs when simple information is put into classes. To the extent the data is immutable, there is little harm that can come of providing access, other than that someone could come to depend upon something that might change. Well, okay, people do that all the time in real life, and when things change, they adapt. And if they are rational, they know when they make a decision based upon something that can change that they might in the future need to adapt. So, it’s a risk management decision, one I think programmers should be free to make. If people don’t have the sensibilities to desire to program to abstractions and to be wary of marrying implementation details, then they are never going to be good programmers.

6
  • 3
    OO programmers don't want everything to be an object. But some things (a lot of things) benefit from encapsulation. I'm having trouble understanding how or where your answer really addresses the question. It seems to just be asserting that the concept isn't specific to OOP and that OOP has other problems and so on and so forth - can you maybe provide a clear example, even if it's just a few lines of pseudocode? Or a whiteboard description of a design that takes this into account? Or anything that would substantiate the assertions here?
    – Aaronaught
    Oct 5, 2013 at 12:33
  • 2
    @Aaronaught: I addressed many (though not all) of the points raised in the question, and referenced a paper about functional programming that looks at modularity in a similar way to the paper in the question. To a large extent, the fact that the concept isn't specific to OOP is the answer to his question (unless I've misunderstood the question entirely). I really didn't talk about OOP having other problems here. You have a good point about providing an example; I'll see if I can come up with a good one. Oct 5, 2013 at 22:00
  • 2
    "Sometimes, a simple data structure is all you need". +1 . Something OOP make sense, sometime it's FP. Oct 7, 2013 at 14:02
  • 1
    @Aaronaught This answer does point out about modularity (which is both encapsulation and reuse) being one of the goals of FP (as discussed in "Why Functional Programming Matters"), therefore making the answer to point (1) of the question a "no".
    – Andres F.
    Oct 7, 2013 at 15:17
  • 2
    @JimmyHoffa information hiding is a sane principle even outside OO. In haskell, I still want users to be allowed to work with a minimal amount of knowledge about any data structure. Sure, having access to the internals is less dangerous because nothing is mutable. But the less a user see about one module/data structure/any abstract concept, the more refactoring opportunities you get. I donc care if a Map is a balanced binary tree or a mouse in a small box in my computer. This is the main motivation behind data hiding, and it is valid outside OO. Oct 9, 2013 at 11:33
12

... 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.

EDIT

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 and cdr:

(... (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 <n> and whose denominator is the integer <d>.

  • (numer <x>) returns the numerator of the rational number <x>.

  • (denom <x>) returns the denominator of the rational number <x>.

and the system will no longer (and should no longer) know of what rationals are made of. This is because cons, car and cdr are not intrinsic to rationals, but make-rat, numer and 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.

2
  • I agree that data hiding is relevant in FP. And, like you say, there are ways of achieving this.
    – Andres F.
    Oct 7, 2013 at 17:12
  • 2
    You just made my point beautifully: You have these functions which aren't hiding data, they are expressions that describe how to get data, thus by having the abstraction in an expression rather than a data field you don't need to worry about hiding the data by creating some complex object with private members or by making your cons values inaccessible, the activities of generationg, retrieving and interacting with rational data is expressed therefore the actual rational data doesn't need to be hidden because changing the data won't change your expressions. Oct 7, 2013 at 22:14
8

Your belief that functional programming lacks data hiding is wrong. It just takes a different approach to hiding data. One of the most common ways of hiding data in functional programming is through the use of polymorphic functions that take a function as an argument. For example, this function

map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs

can only see the outermost structure of the data (i.e. that it is a list) it can't see anything about the data that the list contains and can only operate on the data through the single function that is passed to it.

The function that is passed as an argument is analogous to a public method on the data type that the list contains. It provides a limited way of operating on the data, but does not expose the internal workings of the data type.

6

I'm going to strike out on a limb here and say that the concept is just not relevant in FP the way it is in OO.

tl;dr; The point of data hiding is to ensure responsibilities are maintained where they should be, and you don't have outside actors messing with data they don't have the knowledge for. In FP data is generated by expressions, and in this way you can't mess with the data because it's not mutable properties so much as composable computations which completely changes the rules of the game.


In my experiences with FP; which are admittedly insignificant, I tend to find one stark contrast with OO in what denotes good/common data modeling.

This contrast is that in OO in general you model things to represent your data. Obligatory car analogy:

OO

  • You have a car Object, that correctly hides details about the car such as the AC implementation (is it belt driven, or air-pressure-driven? Consumers shouldn't need to know, so hide it).
  • This car object has many properties and methods that delineate all the facts about the car as well as ways that you can work with a car.
  • This car object has properties which are components of a car that further hide from the overall Car their particular implementations, and their data facts allowing the components of the car to be interchangeable.

The thing to note here is when you're modeling things in an OO format, it's all about representing things as Data. You have objects with properties, many of those properties are objects with more properties. You have a couple methods here and there attached to those objects, but all they really do is usually jigger the properties of the objects this way and that, again it's very data-centric modeling; that is you model your data to be interacted with focussing on structuring it to make available all the points of your data so consumers may change the data this way and that.

FP

  • You have a great deal of computations that allow you to describe behaviours
  • These expressions of behaviour are related in a way that can be translated to the way a cars behaviours are related to eachother, such as a car having accelerate/decelerate, there are two behaviours which oppose eachother in a similar way.

The big difference between OO and FP that constantly strikes me is as I said above the way you model data. In OO as mentioned above you model data as data, in FP you model data as computations, expressions, algorithms, it's more about modeling the activities of your data than the facts of it. Think about basic data modeling in math, it's always about getting an equation that can generate your data, which models your data as the activity that causes it, as opposed to OO is modeling is coming up with a way to represent the data you have. That is much of the distinction between FP and OO.

Remember, for a great deal of time LISP, one of the foundational FP languages, lived with a very small amount of primitive data types. This works because the approach isn't about modeling complex representations of your data so much as computations that generate and express the behaviours of your system.

When I start writing some code in FP, I start by writing code that does something, where as when I start writing code in OO, I start by writing models that describe something. The doing of things is hidden in FP by being expressions, the doing of things is exposed in OO by being described with data, hiding this data limits said exposure.


Back to the question at hand, what does FP say about data hiding, does it appreciate it or disagree with it or what not?

I say it doesn't matter, in OO your data is the guts and important pieces in your program that should be hidden from being meddled with. In FP the guts and knowledge of your system is all hidden away in the algorithms and computations that express the system. These are by definition more or less immutable, the only way to mutate computation expressions are things like macros, but even then you're mutations definitions are expressions themselves which cannot be further meddled with.

2
  • this is brilliant, I really enjoyed reading it. Thanks for your contribution May 1, 2015 at 17:54
  • @Jimmy Hoffa, then by your explanations I would conclude that OOP is better / more convenient for modeling (then implementing) the systems with big/nested/very numerous data structures (types) with complex behaviours, interactions, relations. Whereas FP is better for modeling very numerous operations on rather slim/non-numerous data structures (types) with minimal self behaviour.
    – bob-12345
    Apr 12, 2022 at 7:57
5

There is a bit of a paradox here. Even though functional programming focuses on, well, functions, and frequently has functions that work directly on primitive data types, it tends to have more data hiding than object oriented programming.

How is this so? Think about a nice OO interface that hides underlying data - perhaps collections (I'm trying to pick something nearly ubiquitous). You may not need to know the underlying type of the objects in the collection or type of the object implementing the collection, as long as you know the collection implements, say, IEnumerable. So you have data hiding.

In functional programming, you might write a function that effectively works with an IEnumerable interface, but operates on a primitive data type (or on any data type). But what if the type didn't ever implement the IEnumerable methods? Here's the key, you can always have the "methods" that form the needed pieces of the "interface" be parameters passed in to your function. Or you can put functions together with data and do things in an OO-like way.

Notice that either way you don't have any less data hiding than you have in OO. My general function that works on any type clearly isn't accessing the data in that type - that happens within the functions passed as parameters to the general function, but the general function never peeks inside those functions to see the data.

So, as far as your point 1, I don't think FP and the article really disagree. I don't think your characterization of FP not hiding data is correct. One could implement the design the author preferred in FP, certainly.

As far as point 4 (2 and 3 don't make sense to answer given what I've said for point 1) it varies. It also varies in OO languages, and in many private fields are private by convention rather than enforced by the language.

1
  • In other words: in functional programming, much more is "hidden" by default, simply because it doesn't even exist! Only stuff you explicitly bring into scope is "unhidden". Aug 20, 2014 at 15:56
3

First, thanks for the link to this great article, I did not know this so far, and it gave me some great input about some things which I was discussing with some other software designers from the community in the last years. Here is my opinion about it:

First I want to know if my assessment is correct. Does the FP paradigm and this article philosophically disagree?

FP design focuses very much on data-flow (which is IMHO not so bad as the article may imply). If this is a complete "disagreement" is argueable.

Assuming they disagree, how does FP "compensate" for its lack of data hiding? Perhaps they sacrifice data hiding but gain X, Y and Z. I'd like to know the reasoning for why X, Y and Z are considered more beneficial than data hiding.

IMHO it does not compensate. See below.

Or, assuming they disagree, perhaps FP feels that data hiding is bad. If so, why does it think data hiding is bad?

I don't think most of the FP users or designers do feel or think in this way, see below.

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.

Here is the point - you have probably seen so many OOP systems implemented in a non-functional way that you believe OOP is non-functional. And that's a fallacy, IMHO OOP and FP are mostly orthogonal concepts, and you can perfectly build functional OO systems, which gives you an obvious answer to your question. The classic "object" implementation in FP is done by utilizing closures, and if you want objects to be used in a functional system, the key point is to design them immutable.

So for creating bigger systems, IMHO you can create modules, classes and objects using OO concepts, exactly the way described under "Modularization 2" in the article without leaving the "FP path". You will use the module concept of your favorite FP language, make just all your objects immutable and use the "best of both worlds".

3

TL;DR: No

Does the FP paradigm and this article philosophically disagree?.

No it doesn't. Functional Programming is declarative which is "a style of building the structure and elements of computer programs, that expresses the logic of a computation without describing its control flow." It's less about following the flow chart and more like creating the rules that let the flow arise on it's own.

Procedural programming is much closer to a encoding of a flow chart than Functional programming. It follows that transforms that occurs, and encodes of those transforms into procedures that are executed in order, exactly like the flow in a flow chart describes.

Whereas procedural languages model execution of the program as a sequence of imperative commands that may implicitly alter shared state, functional programming languages model execution as the evaluation of complex expressions that only depend on each other in terms of arguments and return values. For this reason, functional programs can have a freer order of code execution, and the languages may offer little control over the order in which various parts of the program are executed. (For example, the arguments to a procedure invocation in Scheme are executed in an arbitrary order.)

Data Hiding

  • Functional Programming has it's own methods of data hiding, for example think closures. That is data hiding by encapsulation in a closure. It's hard for fields to be any more private data that has been closed over since only the closure has a reference to the data and you cannot refer to it external to the closure.
  • One of the reasons for data hiding is to stabilize the programming interface by hiding mutating data. Functional programming doesn't have mutating data, therefore it doesn't need as much data hiding.
2
  • 3
    "Functional programming doesn't have mutating data, therefore it doesn't need as much data hiding." -- this is a very misleading assertion. You said yourself (and I agree) that one of the reasons for encapsulating behavior is to have control over mutation of data. But to conclude that the lack of mutation almost renders encapsulation useless is a huge stretch. ADTs and data abstraction in general are pervasive in FP literature and systems. Oct 7, 2013 at 15:54
  • I never said it "almost renders encapsulation useless". Those are your thoughts and yours alone. I said you don't need to do as much data hiding because of the lack of mutating variables. This doesn't make encapsulation or data hiding useless, it just reduces it's use because those cases don't exist. All the other cases in which data hiding and encapsulation are useful are still valid.
    – snakehiss
    Oct 7, 2013 at 18:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.