2

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.

  • 2
    Please provide a citation for the statement "Onion architecture is a prerequisite to take the most of Free monad." – Robert Harvey Jul 8 at 21:13
  • @RobertHarvey I don't have a direct citation, it's just my (potentially wrong) understanding of online material. I edited / appended an example article to the question and my summary on it – KolA Jul 9 at 6:41
  • OK, I read the article (skimmed it as best as I can, not being a Haskell expert), and I think you're confusing this author's casual use of the word "onion" with the "onion architecture" proposed by Jeffery Palermo. – Robert Harvey Jul 9 at 14:17
  • Jeffery Palermo says of Onion Architecture: "The main premise is that it controls coupling. The fundamental rule is that all code can depend on layers more central, but code cannot depend on layers further out from the core. In other words, all coupling is toward the center. This architecture is unashamedly biased toward object-oriented programming, and it puts objects before all others." – Robert Harvey Jul 9 at 14:17
  • 1
    So while it's possible that there are similarities between the onion proposed by the FP author and the "Onion Architecture" used in OO systems, I think the use of the term "onion" is entirely coincidental. – Robert Harvey Jul 9 at 14:19
1

Mostly what free monads are in relation to an onion architecture are a way to easily swap the outer layers of the onion. In practice, usually you swap in a pure interpreter implementation for unit testing, and an impure implementation for normal runtime. However, it is also sometimes used for swapping out things like different database or logging methods.

The main advantage over OOP-style dependency injection is that with free monads, you inject your dependencies only once at the edge of the world at interpretation time. With OOP-style, you have to pass that dependency everywhere throughout the program specification part of the code. With free monads, your code reads more concisely, because you don't have extra function arguments that do nothing but pass dependencies to lower layers to enable unit testing.

That advantage doesn't go away even if your code is "undisciplined," although discipline obviously benefits any code in any paradigm.

  • Yes that's true and I experienced it first hand: pure core relieved me from need to pass dependencies around (which would be done by a DI container if it was C# but that's an apples to oranges comparison). It also helped to segregate domain logic from "async" and db transactions - something that DI wouldn't do. – KolA Jul 9 at 6:57
  • What I probably got wrong from my online research is that Free somehow magically helps to achieve nice and clean pure core design. I guess that's not the case: in C# I can't claim that my architecture is pure if I simply abstract away all impurities to interfaces and use DI container – KolA Jul 9 at 7:04
  • 1
    It definitely helps achieve a pure core design, because your most impure code naturally wants to go into the interpreter. However, you still have a bunch of pure values which are a sort of proxy for impure code, and like IO values, you will have a much cleaner design if you don't pass them all over the place. – Karl Bielefeldt Jul 9 at 11:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.