(Pre-req: Functional core, imperative shell architecture)
- The functional core is more testable and more composable than the imperative shell or effectful code
- Sometimes you can easily extend the functional core by lifting out side-effects to calling code
- Sometimes, the side-effects are entangled inside business logic
- This can be dealt with in different ways depending on the type of the side-effects
Consider the following categories of effects:
- Effects that can be delayed
- Effects that depend on each other
- Effects where the result is needed at once
Function que
to add lambda to queue, like que (fun () -> <effectful thing>)
, then run the queue in the imperative shell, like IO.run_queue
Function seq
for a sequence of lambdas where each is run only if previous returns true. Harder to read than “normal” program flow.
When the result is needed at once, consider either yield/generator system (using OCaml’s new effect system), or an IO module that is mocked during unit testing.
In functional programming, and Haskell, you also have strategies like Free Monad, or tagless-final, to life out side-effects. Strategies that do increase the complexity of the code, and also don’t differ between the different categories of effects.