- Proper error handling is extremely difficult, this is why many different approaches have been invented.
- Exceptions represent a language-specific mechanism for derailing the evaluation to an additional implicit control flow when something unexpected happened.
- Errors are mostly implicit and indicate expected or possible erroneous situations that are not considered bugs.
- Exceptions and errors are not the same but sometimes exceptions are used to indicate an erroneous situation.
- In some practices, exceptions are prohibited. In some languages like Rust, exceptions are absent as a language feature.
- The Free monad approach allows for having exceptions although prohibiting them would be a better design choice.
- The key to proper error handling is hidden in the principle we know nicely: divide and conquer.
- We divide our application not only into architectural layers but also into error domains.
- Each error domain should have its own way to deal with errors and exceptions, and there should be a conversion between the representations on the boundary between error domains.
- In functional programming, the Dependency Inversion principle plays the same role: it helps to make some business logic depend on an interface and be freed from implementation details.
- Implementing the DI principle means having some mechanism for an interface and being able to inject a dependency hidden behind this interface into the business logic.
- There are numerous ways to do interfaces in FP, and neither of them is perfect.
- There are API-like interfaces, scenario-like interfaces and combinatorial ones.
- Free monad interfaces and GADT interfaces are very similar although Free monad ones are scenario-like whereas GADT ones are API-like.
- Service Handle seems to be the simplest FP interface possible, not considering a bare function itself.
- The ReaderT pattern is an advanced version of the Service Handle pattern that utilizes the Reader monadic environment to store the handle.
- Final Tagless which is also known as the mtl style, has many implementations and is based on extra type-level features such as type classes and phantom types.
- Knowing best practices and design patterns helps the developer to navigate across solutions and make proper decisions.