I've written my first moderately large project in functional style (in F#) and can see the advantages. The main challenge was to achieve the "Onion" architecture i.e. large and "smart" pure core / thin and "dumb" impure shell. It took most of the time but also yielded largest benefits (better testability, correctness, code reusability etc.) and I want to know more.
I began to search for common approach to segregate IO/effects from pure logic and found a lot of material about Free Monads, mostly in Haskell (I know that F# doesn't have HKT's nor native free monads but anyway).
From what I've read, do I understand it correctly that:
- Free monad is not the recipe to achieve Onion architecture, but instead Onion architecture is a prerequisite to take the most of Free monad
- Free monad on top of messy undisciplined code has little if any advantage over plain Dependency Injection
For example if results of IO operations (abstracted by Free monad or not) used for branching/decision making interleaved with other logic without any discipline - in short, if function doesn't reduce to a flat list or a very simple tree of instructions that can be further simplified by the interpreter of free monad.
Am I missing something fundamental here?
Here's one of the articles I can refer to: http://degoes.net/articles/modern-fp
In "Enjoying the Onion" paragraph author claims that "We are further able to completely untangle different aspects of our program ... finally, we are able to consolidate knowledge that would otherwise be distributed throughout the program ..." - allegedly thanks to Free?
But from what I can see from the article he first carefully designed the onion and the concise File algebra and only then got the aforementioned benefits (different aspects untangled/knowledge consolidated/opportunity to optimize some compound operations like Rename in interpreter etc.). When this already achieved I still struggle to see how Free is "far better solution" (a citation from "Summary") than say a DI container.