Welcome to the world of Kotlin and its amazing libraries! Today, we will be discussing one of the most important concepts in the Arrow Kt library: the Eval Monad. In this beginner’s guide, we will explore what the Eval Monad is, what it does, and how to use it in your Kotlin applications.
Exploring Eval Monad
In simple terms, a monad is a design pattern in functional programming that allows you to write code that can handle computation in a more composable and predictable way. It’s like a toolbox that helps you build complex programs by breaking them down into smaller, easier-to-manage parts. To know more about monad checkout our previous post to know it in depth.
What is the Eval Monad❓
Arrow KT’s Eval monad is a powerful tool for controlling the timing and execution of code in a Kotlin program. It’s designed to handle tasks that are either:
- Heavy computations that you want to defer until they’re actually needed
- Tasks that you want to run only once, even if you call them multiple times
By using the Eval monad, you can ensure that your code runs efficiently, without doing any unnecessary work.
Why use the Eval Monad ❓
There are several reasons, why you might want to use the Eval Monad in your Kotlin applications. Some of the most common use cases include:
- Performance optimization: By delaying the evaluation of computation until it is needed, you can improve the performance of your application.
- Lazy evaluation: If you only need the result of a computation under certain conditions, you can use the Eval Monad to ensure that the computation is only performed when necessary.
- Memoization: If you have a computation that is expensive to perform, you can use the Eval Monad to cache the result and avoid having to recompute it every time it is needed.
To use Eval
monad you need to implement Arrow Kt in your project, you can add the following dependency to your
build.gradle
file:
dependencies { implementation "io.arrow-kt:arrow-core:$arrow_version" }
For detailed information on how to implement Arrow.Kt in your project and latest version be sure to check out the Arrow.Kt website and documentation.
Exploring Eval Monad 🌐
There are three different types of Eval monad: now
, later
, and always
. Let’s take a closer look at each one:
Now
: Computes the value immediately.Later
: Defers computation until it is actually needed.Always
: Recomputes the value each time it is needed.
Let’s understand each of these in detail with examples:
Exploring Eval Monad
Now
💥
The now
type of Eval monad runs its computation immediately, when it’s called. This is the equivalent of just calling a function normally in Kotlin.
For example, if you want to calculate the result of a mathematical expression and store it in a variable, you could use the now
type of Eval monad like this:
val result = Eval.now(1 + 2 ).value() println(result) // 3
In this case, the expression 1 + 2
is calculated immediately, and its result is stored in the result
variable.
Let’s check with an real example 🚁
💡 Have you ever wanted to calculate something and get the result right away? That’s where now
comes in! The now
method evaluates the lazy computation immediately, just like a regular function.
Let’s say, you want to get the current temperature in your city. You can use the now
method to fetch the temperature right away and not wait for any other computations to be done.
Here’s a code example in Kotlin:
val temperature = Eval.now(25) println("The temperature is: ${temperature.value()}")
Output:
The temperature is: 25
Later 💭
The later
type of Eval monad defers its computation until its result is actually needed. This means that the computation is only executed once, when its result is called, even if the later
instance is used multiple times.
Here’s an example of how you could use the later
type of Eval monad to cache the result of a heavy computation:
val computation = Eval.later { println("Running the computation...") 1 + 2 } println("First call") println(computation.value()) // Running the computation... 3 println("Second call") println(computation.value()) // 3
In this example, you can see that the message “Running the computation…” is only printed once, even though we’re calling the computation
instance multiple times. This is because the computation is only executed once, and its result is cached for subsequent calls.
Here’s a real-life example of the later
monad in action 💡
Let’s say you’re making a food delivery app and you want to calculate the total cost of a customer’s order. But, you also want to include a $5 delivery fee.
So, you have two values to calculate: the cost of the food and the delivery fee.
Normally, you would add these two values together to get the final cost. But with the later
monad, you can delay the calculation of the delivery fee until the food cost is known.
Here’s how you could use the later
monad to implement this in code:
val foodCost = Eval.later { /* calculate the cost of the food */ } val deliveryFee = Eval.later { 5 } val totalCost = foodCost.map { cost -> cost + deliveryFee.value() }
Job Offers
With this implementation, the delivery fee is only calculated when deliveryFee.value()
is called, which happens when totalCost
is evaluated.
This is a great example of how the later
monad can be used to defer calculations until the values they depend on are known. 🤓
Always 👨💻
Always
is one of the three types of Eval
monads in the Arrow Kotlin library. As the name suggests, always
evaluates to a constant value, no matter when you run it.
Think of it like this: you have a function that takes a string input, and you want to always return a message that says “Hello, $input”. You could write that function like this:
fun sayHello(name: String): Eval<String> = Eval.always { "Hello, $name" }
And every time you run this sayHello
function, you’ll get the same result:
val greeting = sayHello("John").value() println(greeting) // prints "Hello, John"
Let’s take real use case of always
:
Suppose you have an app that allows users to search for a product, and you have a database of products. The database takes time to fetch the data, and it’s important that you only fetch the data once.
You can use the always
function in this case to ensure that the database is only queried once, and the results are cached for future use. Here’s how the code would look like:
val fetchProducts: Eval<List<Product>> = Eval.always { println("Querying the database...") listOf(Product("product1"), Product("product2"), Product("product3")) } val firstCall = fetchProducts.value() println("Result of first call: $firstCall") val secondCall = fetchProducts.value() println("Result of second call: $secondCall")
In this example, the fetchProducts
is an Eval
instance that represents the calculation of fetching the products from the database. The always
function is used to create the instance, and the function passed to always
will only be executed once.
When we call fetchProducts.value()
for the first time, it will print “Querying the database…” as it is executing the calculation. But when we call it for the second time, it won’t execute the calculation again, it will just return the cached result from the first call, as the always
function ensures that the calculation is executed only once.
So, in this example, the always
function is used to cache the result of an expensive computation and ensure that it is only executed once.
💡 Keep in mind, that
always
is ideal for caching the result of a calculation that is guaranteed to always be the same and is too expensive to perform multiple times.
In Conclusion 👐
The Eval
monad is a powerful tool in the Arrow library that provides a way to defer computations and keep track of actions performed. With its three types now
, later
, and always
, you can choose the best type for your use case based on your requirements.
In this article, we explored the concept of the Eval
monad and its different types with real-life examples. I hope this article helped you understand the Eval
monad
Let’s give this article a round of applause 🎉 and don’t forget to follow for more amazing content!
This article was originally published on proandroiddev.com