←back to thread

296 points gyre007 | 1 comments | | HN request time: 0s | source
Show context
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 #
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 #
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 #
1. nybble41 ◴[] No.21286235[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.