Most active commenters
  • cryptica(4)

←back to thread

296 points gyre007 | 11 comments | | HN request time: 0.629s | source | bottom
1. cryptica ◴[] No.21281989[source]
OOP was designed to prioritize encapsulation at the expense of referential transparency. Functional programming was designed to prioritize referential transparency at the expense of encapsulation.

You cannot have referential transparency and encapsulation at the same time.

In order to prevent mutations (which is a requirement of FP), a module cannot hold any state internally; this necessarily means that the state must be passed to each module action from the outside. If state has to be passed to each module action from the outside, then this necessarily means that the outside logic needs to be aware of which state is associated with which action of which child module. If higher level modules need to be aware of all the relationships between the logic and state of all lower level (child) modules, that is called 'leaky abstraction' and is a clear violation of encapsulation.

Encapsulation (AKA 'blackboxing') is a very important concept in software development. Large complex programs need to have replaceable parts and this requires encapsulation. The goal is to minimize the complexity of the contact areas between different components; the simpler the contact areas, the more interchangeable the components will be. It's like Lego blocks; all the different shapes connect to each other using the same simple interface; this gives you maximum composability.

Real world software applications need to manage and process complex state and the best way to achieve this is by dividing the state into simple fragments and allowing each fragment to be collocated with the logic that is responsible for mutating it.

If you design your programs such that your modules have clear separation of concerns, then figuring out which module is responsible for which state should be a trivial matter.

replies(5): >>21282691 #>>21283116 #>>21284562 #>>21287900 #>>21295632 #
2. overgard ◴[] No.21282691[source]
Encapsulation is desirable because it limits the possibility space of what can operate on a set of data. Referential transparency is desirable because pure programs are much easier to reason about. If I understand what youre saying, it seems youre saying referential transparency and encapsulation are at odds and encapsulation is more valuable, but I disagree. Hiding state maybe keeps things tidy and enforces that you need to use the API, but its not really the point IMO. The point of encapsulation is managing state mutations. Hiding state is only a small part. You dont need to hide state as much when its immutable because then you don’t need to care what other code is doing with your emitted data structures because it doesn’t effect you.
replies(1): >>21291516 #
3. stickfigure ◴[] No.21283116[source]
I don't quite follow this. You can create (very complicated) immutable objects that encapsulate their state, and provide methods that return new immutable objects with different - and still fully encapsulated - state. Vavr is a good example.
replies(1): >>21283433 #
4. cryptica ◴[] No.21283433[source]
Yes you can reduce and map a large state object into a smaller and simpler object before you pass it down to a child component but the encapsulation is still leaky because the parent component needs to know the implementation details of each action of a child component in order to use them correctly (for example, the parent component needs to know how the different actions of the child relate to each other in terms of how they affect the state); it creates a large contact area between components which creates tight coupling.

The idea of blackboxing/encapsulation is that the parent component should know as little as possible about the implementation of its child components.

replies(2): >>21286235 #>>21287510 #
5. proc0 ◴[] No.21284562[source]
" the more interchangeable the components will be. It's like Lego blocks; "

This is precisely the reason why pure FP is prioritizing referential transparency. Even if objects are perfectly encapsulated, with enough complexity, because other objects will depend on that information, and because that information mutates and changes over time, this is bound to cause some errors.

Compilers can't check program correctness because of the halting problem, so FP aims to give the programmer some patterns + laws to help better reason across this "higher" dimension of moving parts.

replies(1): >>21291677 #
6. nybble41 ◴[] No.21286235{3}[source]
Do you have a more concrete example? So far as I can see there is no reason why functional programming would require a parent component to know anything about how its child components interact with the state. The tight coupling you are describing sounds completely foreign to me as a Haskell programmer.
7. senderista ◴[] No.21287510{3}[source]
I think some of the spirit of encapsulation (i.e., decoupling from implementation) is achieved by polymorphic functional interfaces, e.g. type classes in Haskell.
8. ummonk ◴[] No.21287900[source]
In my view, the new hooks paradigm in React combines the best of both worlds in FP and OOP.
9. cryptica ◴[] No.21291516[source]
Encapsulation doesn't necessarily mean hiding state. It means hiding the implementation details of how actions mutate the state. The same action called on a different kind of module instance can end up mutating the instance's internal state in a completely different way. The higher level logic should not be concerned with how a module performs an action.
10. cryptica ◴[] No.21291677[source]
>> Even if objects are perfectly encapsulated, with enough complexity, because other objects will depend on that information, ...

I would argue that when other objects from different parts of the code depend on the same state and there is no clear hierarchy or data flow direction between those objects, then that is going to cause problems regardless of whether the language is OOP or FP. The problems will manifest themselves in different ways but it will be messy and difficult to debug in either case (FP or OOP) because this is an architectural problem and not a programming problem. It will require a refactoring.

OOP helps to reduce architectural problems like this because it encourages developers to break logic up into modules which have distinct, non-overlapping concerns.

11. cannabis_sam ◴[] No.21295632[source]
Encapsulation is trivial in FP tho.