Functional Programming Self-Affirmations
Dmitrii Kovanikov emphasizes the importance of functional programming principles in Swift, advocating for practices that enhance code quality, reliability, and maintainability through better data handling and error management.
Read original articleDmitrii Kovanikov discusses the relevance of functional programming principles in mainstream programming languages, particularly Swift. He highlights five self-affirmations related to functional programming: "Parse, don’t validate," "Make illegal states unrepresentable," "Errors as values," "Functional core, imperative shell," and "Smart constructor." Each principle emphasizes improving code quality and maintainability. For instance, "Parse, don’t validate" suggests enriching data through parsing rather than merely validating it, while "Make illegal states unrepresentable" advocates for designing logic and types to prevent bad states. The concept of "Errors as values" promotes returning error values instead of raising exceptions, enhancing predictability. The "Functional core, imperative shell" principle encourages isolating pure functions from side effects, making testing easier. Lastly, "Smart constructor" focuses on ensuring valid data construction, which can optimize performance and clarity. Kovanikov concludes that while these ideas are rooted in functional programming, they can be beneficially applied in imperative programming as well, leading to simpler and more robust code.
- Functional programming principles can enhance code quality in mainstream languages.
- Parsing enriches data, while validation merely checks correctness.
- Designing to prevent illegal states can reduce bugs and improve code reliability.
- Returning error values instead of exceptions leads to more predictable error handling.
- Isolating pure functions from side effects simplifies testing and improves clarity.
Related
I Probably Hate Writing Code in Your Favorite Language
The author critiques popular programming languages like Python and Java, favoring Elixir and Haskell for immutability and functional programming benefits. They emphasize personal language preferences for hobby projects, not sparking conflict.
What's Functional Programming All About?
Functional programming is often misunderstood, focusing on irrelevant details. It emphasizes complexity control and clearer structures, contrasting with imperative programming's challenges, promoting better error handling and refactoring.
Functional Programming to Help Write Efficient, Elegant Web Applications
Functional programming enhances web application development through immutability and pure functions, improving predictability and maintainability. Kotlin supports both paradigms, facilitating gradual adoption of functional concepts for clearer logic.
Functional PHP (2015)
The author advocates for using functional programming in PHP, highlighting benefits like improved modularity, reduced complexity, easier debugging, and enhanced maintainability through small, single-purpose functions and higher-order functions.
Functional Programming in Go
The article highlights functional programming in Go, focusing on map, filter, and reduce operations. It explains first-class functions and provides code examples, promoting their use for elegant and efficient coding.
- There is skepticism about the practicality of making illegal states unrepresentable, especially in dynamic domains with evolving data structures.
- Many commenters discuss the merits and drawbacks of treating errors as values versus using exceptions, highlighting the importance of context in error handling.
- Some agree that functional programming principles can enhance code quality, even in non-functional languages, but emphasize the need for discipline to implement them effectively.
- Several commenters express that while the ideas presented are valuable, they are not new and are already well-documented in existing literature.
- There is a consensus that the principles discussed can lead to better code organization and reliability, but practical implementation can be challenging.
On the surface, it makes complete sense, the non-locality of exceptions make them hard to reason about for the same reasons that GOTO is hard to reason about, and representing failure modes by values completely eliminates this non-locality. And in purely functional languages, that's the end of the story. But in imperative languages, we can do something like this:
def my_effectful_function():
if the_thing_is_bad:
# do the failure thing
raise Exception
# or
return Failure()
return Success()
and a client of this function might do something like this: def client_function():
...
my_effectful_function()
...
and completely ignore the failure case. Now, ignoring the failure is possible with both the exception and the failure value, but in the case of the failure value, it's much more likely to go unnoticed. The exception version is much more in line with the "let it crash" philosophy of Erlang and Elixir, and I'm not sure if the benefits of locality outweigh those of the "let it crash" philosophy.Have any imperative folks here successfully used the "errors as values" idea?
FP-first/only languages tend to push you in these directions because it makes programming with them easier.
In languages where FP is optional, it takes discipline and sometimes charisma to follow these affirmations/patterns/principles.. but they’re worth it IMO.
Especially when dealing with a fast changing domain, having to support different versions of data shapes across long time periods: dynamic data definitions are more economic and will still provide sufficient runtime protection.
"Errors as values" - what is an error? I see this pattern misused often, because not enough thought was put into the definition of an error.
"Disk is Full" vs. "data input violates some business rule" are two very - very - different things and should be treated differently. Exceptions are the right abstraction in the first case. It's not in the second case.
"Functional core, imperative shell" - 100% agreement here.
> To me, this simply makes more sense: isn’t it objectively better to get a finite and predictable error value from a function than an unspecified exception that may or may not happen that you still have to guard against?
Whether an error is returned as a value or thrown is orthogonal to whether it is finite and predictable. Java has checked exceptions. In Swift you also can specify the exceptions that a function may throw. How is it any less predictable than return values?
Semantically, a thrown exception is simply a return value with debug information that gets automatically returned by the caller unless specified otherwise. It is simply a very handy way to reduce boilerplate. Isn't it objectively better to not write the same thing over and over again?
One way to achieve "Make illegal states unrepresentable" is by using "refined" types, a.k.a. highly constrained types. There is a "refined" library in both Haskell and Scala and the "iron" library for Scala 3.
This is a nice ideal to shoot for, but strict adherence as advocated in the article is a short path to algorithmic explosions and unusable interfaces on real life systems.
For example, if you have two options that are mutually incompatible, this principle says you don't make them booleans, but instead a strict enum type populated with only legal combinations of the options. A great idea until you have 20 options to account for and your enum is now 2^16 entries long. Then your company opens a branch in a different country with a different regulatory framework and the options list grows to 50 and you code no longer fits on a hard drive.
linked from the article:
https://www.javiercasas.com/articles/functional-programming-...
State Skeptic: Yes, But! How do you compose the 'pure core + impure shell' pieces?
FPN: Obviously, you compose the pure pieces separately. Your app can be built using libraries built from libraries.... And, then build the imperative shell separately.
My take is that the above solution is not so easy. (atleast to me!) (and not easy for both FP and non-FP systems).
Take an example like GUI components. Ideally, you should be able to compose several components into a single component (culminating in the app) and not have a custom implementation of a giant state store which is kept in something like Redux and you define the views and modifiers using this store.
Say, you have a bunch of UI components each given as a view computed as a function from a value and possible UI events which can either modify the value, remain unhandled or configurable as either. Ex: dialog box which handles text events but leaves the 'OK' submission to the container.
There are atleast two different kinds of composability (cue quote in SICP Ch1 by Locke) - aggregation and abstraction. Ex: Having a sequence of text inputs in the document(aggregation) and then abstracting to a list of distances between cities. This abstraction puts constraints on values of the parts, both individually(positive number) and across parts(triangle inequality). There is also extension/enrichment, the dual of abstraction.
This larger abstracted component itself is now a view dependent on a value and more abstract events. But, composing recursively leads to state being held in multiple layers and computations repeated across layers. This is somewhat ameliorated by sharing of immutable parts and react like reconciliation. But, you have to express your top->down functions incrementally which is not trivial.
I don't see the value of learning this stuff one random blog post at a time.
There are many books and established blogs with long series of material to give you an education.
Ah yes, because using constructors to ensure that new objects are in a valid state is virtually unheard of in object-oriented programming.
Related
I Probably Hate Writing Code in Your Favorite Language
The author critiques popular programming languages like Python and Java, favoring Elixir and Haskell for immutability and functional programming benefits. They emphasize personal language preferences for hobby projects, not sparking conflict.
What's Functional Programming All About?
Functional programming is often misunderstood, focusing on irrelevant details. It emphasizes complexity control and clearer structures, contrasting with imperative programming's challenges, promoting better error handling and refactoring.
Functional Programming to Help Write Efficient, Elegant Web Applications
Functional programming enhances web application development through immutability and pure functions, improving predictability and maintainability. Kotlin supports both paradigms, facilitating gradual adoption of functional concepts for clearer logic.
Functional PHP (2015)
The author advocates for using functional programming in PHP, highlighting benefits like improved modularity, reduced complexity, easier debugging, and enhanced maintainability through small, single-purpose functions and higher-order functions.
Functional Programming in Go
The article highlights functional programming in Go, focusing on map, filter, and reduce operations. It explains first-class functions and provides code examples, promoting their use for elegant and efficient coding.