New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Free Monads Sample #6
Conversation
Thanks @raulraja for helping on understanding and polishing some details on this. You always become very helpful 👏 . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
README.md
Outdated
@@ -51,7 +51,9 @@ Higher Kinds to make our code depend on typeclass constrained behaviors, leaving | |||
which concrete types to use to the moment when we want to run the code. | |||
[You will really want to look at this PR to have a very good and detailed description of what tagless-final is](https://github.com/JorgeCastilloPrz/KotlinAndroidFunctional/pull/2). | |||
## Free Monads | |||
TBA. This is going to be another cool approach using FP, but still not ready. | |||
This FP style is very trendy. We are applying it over Android thanks to Kategory here, on the `free-monads` project module. It's highly recommended to take a look at [this PR](https://github.com/JorgeCastilloPrz/KotlinAndroidFunctional/pull/6) in order to understand the approach. | |||
**Free Monads** is based on the idea of composing an **AST** (abstract syntax tree) of computations with type `Free<T>` which will never depend on implementation details but on abstractions defined by an algebra, which is an algebraic data type (ADT). We are defining it through a `sealed` class on this sample. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Free<S, A>
where S
is your algebra
README.md
Outdated
TBA. This is going to be another cool approach using FP, but still not ready. | ||
This FP style is very trendy. We are applying it over Android thanks to Kategory here, on the `free-monads` project module. It's highly recommended to take a look at [this PR](https://github.com/JorgeCastilloPrz/KotlinAndroidFunctional/pull/6) in order to understand the approach. | ||
**Free Monads** is based on the idea of composing an **AST** (abstract syntax tree) of computations with type `Free<T>` which will never depend on implementation details but on abstractions defined by an algebra, which is an algebraic data type (ADT). We are defining it through a `sealed` class on this sample. | ||
Those ops can be combined as blocks to create more complex ones. Then, we need an **interpreter** which will be in charge to provide implementation details for the moment when the user decides to run the whole AST providing semantics to it and a `Monad` instance to resolve all the effects. The user has the power of chosing which interpreter to use and which monad instance he wants to solve the problem. That enables testing, since we can easily remove our real side effects in the app at our testing environment by switching the interpreter by a fake one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolve all effects / perform execution of effects in a controlled context
README.md
Outdated
TBA. This is going to be another cool approach using FP, but still not ready. | ||
This FP style is very trendy. We are applying it over Android thanks to Kategory here, on the `free-monads` project module. It's highly recommended to take a look at [this PR](https://github.com/JorgeCastilloPrz/KotlinAndroidFunctional/pull/6) in order to understand the approach. | ||
**Free Monads** is based on the idea of composing an **AST** (abstract syntax tree) of computations with type `Free<T>` which will never depend on implementation details but on abstractions defined by an algebra, which is an algebraic data type (ADT). We are defining it through a `sealed` class on this sample. | ||
Those ops can be combined as blocks to create more complex ones. Then, we need an **interpreter** which will be in charge to provide implementation details for the moment when the user decides to run the whole AST providing semantics to it and a `Monad` instance to resolve all the effects. The user has the power of chosing which interpreter to use and which monad instance he wants to solve the problem. That enables testing, since we can easily remove our real side effects in the app at our testing environment by switching the interpreter by a fake one. | ||
|
||
# Goals and rationales |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rationale
Free Monads Sample
Fixes #7
Motivations
The only purpose of using Free is to be able to model your app / system logic in an abstract way depending on
Free
instead of concrete types, and provide semantics (real effect-causing implementations) to it through an interpreter later on, the moment you want to run it.The abstract execution tree that you created declaratively based on
Free
is called AST (Abstract syntax tree), and it is unfolded step by step byFree
using an interpreter to provide semantics to each one of the steps (operations).This allows you to decouple contracts from implementations and be able to swap side effect implementations by fakes at testing time easily. You would just need to provide a different interpreter which does not include real side effects.
More concise definition extracted from Functional and Reactive Domain Modeling book:
"The interpreter provides the denotation to each of the elements of algebra that your free monad has recursively built within your composed DSL. And the interpretation may also include side effects."
How to go Free with KΛTEGORY
KΛTEGORY enables Free and effects like IO or Async related effects to help us on this matter. The process of modeling a complete system or just a part of it using
Free
is composed of 4 different steps:Define your algebra of operations: You need to declare the operations that you want to model on your system / layer where you are implementing
Free
. It is an algebraic data type (ADT) that I am declaring using a Kotlinsealed class
. The algebra declares a constrained amount of operations that will be available and composable just in case you need to create more complex ones.This is an example of an Algebra, where each one of the types on the sealed hierarchy represents an atomic operation:
We have a closed hierarchy of ops available here. Each one has it's particular return type
depending on what's gonna do. So
GetAll
is an algebra ofList<CharacterDto>
because it'spotentially returning a list of characters. Another example:
HandlePresentationEffects
wouldrepresent the operation causing effects to render use case results on screen (which would be
equivalent to presentation logic) so, since it is representing a side effect by itself, it needs to return
Unit
.Lift everything to the context of Free: We need to lift each one of the algebra ops to the context of
Free
to be able to compose our app AST using those. This is how I am doing it:As you can see, I am just providing handful utility pure functions to lift each operation to
Free
. Now we can start using those in our app to concatenate code through different layers and compose the whole AST.Provide semantics with the Interpreter: Once you have your whole AST depending on
Free
and never on concrete types, we need to create a interpreter to provide those details.This is my interpreter:
So the main point of interest here is the
interpreter()
function, which is providing aninterpreter to use to resolve each one of the steps on the AST when the user decides to "unfold" it.
It's just a simple
when
statement matching over the operation type and running the requiredeffects in response by using the chosen monad instance.
So literally, the interpreter here is in charge of providing all the implementation details / side
effects that we wanted to abstract.
The key point about which parts of your system should be declared as operations in the
algebra and therefore abstracted using
Free
, is whether you need to replace thoseimplementations at runtime or at testing time. This was my baseline thought used when
implementing this sample.
After all,
Free
is just a way of decoupling system declarations from implementation detailsused to run them.
Unfold the AST using the interpreter: Finally you would just need to run the system passing in the interpreter to be used to provide semantics, and the monad implementation that you are chosing to do all the work inside of it.
The
getSuperHeroes()
function is just the beginning of the AST and it's gonna return a composedFree
based tree. On this example I am chosing to use an instance ofMonadError
tofoldMap
the AST and use it on the interpreter provided on step 3. NowFree
is able to run the whole chain since it has been provided implementation details (interpreter) and a monad instance to use to resolve those.So cheers, there we have our Free Monads sample over Android! 🎉
Recommended bibliography