Information Flexibility
In order for a system to remain flexible, the Information stored and propigated therein needs to be extensible. This is one instance where Object Oriented Programming has an advantage over Functional Programming. The convention of interacting directly with raw values in FP makes extending those values difficult, whereas objects are often trivial to extend. In order for functional programs to remain flexible, values which are used in many disparate locations should be hidden behind accessor functions, like Lenses, such that the value’s structure may be edited without widescale changes to the codebase.
An illustrative example
One of the worst flexibility issues I’ve ever seen happened at a medical informatics company where I was employed. Their system used a formatted string of information to ID medical records, which was modified and parsed ad hoc all over the codebase. Though they were using OOP, the string was not abstracted into an object, so they had no formal method to extend the value to represent new Information. When they were asked to combine the servers for multiple sister hospitals into one joint coalition, they found that the ID’s at different hospitals could collide, and the only way to fix it would be to add a hospital identifier to the string. The change cost millions of dollars by one developers estimate.
Information flexibility issues are rarely this egregious, but they are damaging enough to elicit some care when interacting with a data type from too many locations in code .
Creating Flexible Data Types
In OOP, the solution is obvious: Abstract the data by encapsulating it within an Object. Solving the problem in FP is more open ended. The most trivial solution is to create low level accessor functions to read and modify the data type. These accessors may be written in the form of Lenses. Whenever some code must interact with a value, it should do so through the accessor functions, such that alterations to the structure of the value do not affect the code, assuming that a backwards compatible change to the accessor is possible.
It should be noted that the flexibility of lenses may fail when operating on collections. Imagine a lens for a task list which receives the tasks as an array of strings.
const list: string[] = getTitles(tasks);
If the array were sorted and then returned, the lens function would need to heuristcally determine which tasks went where.
const list: string[] = getTitles(tasks);
const sorted = setTitles(tasks, list.sort(sortFn));
If the tasks titles were also updated in some way, this heuristic could fail, causing non-deterministic results.
This issue rarely occurs when dealing with the items on a fixed data-structure, like an object or a tuple, but may still happen if the values have an interchangable type.
const rightInBoth = setLeft(pairTuple, getRight(pairTuple));
In cases where a collection or data structure allows for elements to be rearranged, such that the elements identity cannot be determined solely by its position in the collection, some identifier, like a GUID, should travel with the elements when removed by a lens.