-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Add Eval documentation #1816
Merged
Merged
Add Eval documentation #1816
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
--- | ||
layout: docs | ||
title: "Eval" | ||
section: "data" | ||
source: "core/src/main/scala/cats/Eval.scala" | ||
scaladoc: "#cats.Eval" | ||
--- | ||
# Eval | ||
|
||
Eval is a data type for controlling synchronous evaluation. | ||
Its implementation is designed to provide stack-safety at all times using a technique called trampolining. | ||
|
||
There are two different factors that play into evaluation: memoization and laziness. | ||
|
||
Memoized evaluation evaluates an expression only once and then remembers (memoizes) that value. | ||
Lazy evaluation refers to when the expression is evaluated. | ||
We talk about eager evaluation if the expression is immediately evaluated when defined and about lazy evaluation if the expression is evaluated when it's first used. | ||
|
||
For example, in Scala, a `lazy val` is both lazy and memoized, a method definition `def` is lazy, but not memoized, since the body will be evaluated on every call. | ||
A normal `val` evaluates eagerly and also memoizes the result. | ||
|
||
`Eval` is able to express all of these evaluation strategies and allows us to chain computations using its `Monad` instance. | ||
|
||
#### Eval.now | ||
|
||
First of the strategies is eager evaluation, we can construct an `Eval` eagerly using `Eval.now`: | ||
|
||
|
||
```tut:book | ||
import cats.Eval | ||
import cats.implicits._ | ||
|
||
|
||
val eager = Eval.now { | ||
println("Running expensive calculation...") | ||
1 + 2 * 3 | ||
} | ||
``` | ||
|
||
|
||
We can run the computation using the given evaluation strategy anytime by using the `value` method. | ||
|
||
```tut:book | ||
eager.value | ||
|
||
``` | ||
|
||
#### Eval.later | ||
|
||
If we want lazy evaluation, we can use `Eval.later`: | ||
|
||
```tut:book | ||
val lazyEval = Eval.later { | ||
println("Running expensive calculation...") | ||
1 + 2 * 3 | ||
} | ||
|
||
lazyEval.value | ||
|
||
lazyEval.value | ||
``` | ||
|
||
Notice that "Running expensive calculation" is printed only once, since the value was memoized internally. | ||
|
||
#### Eval.always | ||
|
||
If we want lazy evaluation, but without memoization akin to `Function0`, we can use `Eval.always` | ||
|
||
```tut:book | ||
val always = Eval.always { | ||
println("Running expensive calculation...") | ||
1 + 2 * 3 | ||
} | ||
|
||
always.value | ||
|
||
always.value | ||
``` | ||
|
||
Here we can see, that the expression is evaluated every time we call `.value`. | ||
|
||
|
||
### Chaining lazy computations | ||
|
||
One of the most useful applications of `Eval` is its ability to chain together computations in a stack-safe way. | ||
You can see one such usage when looking at the `foldRight` method found in [`Foldable`](foldable.html). | ||
Another great example are mutual tail-recursive calls: | ||
|
||
```tut:book | ||
object MutualRecursion { | ||
def even(n: Int): Eval[Boolean] = | ||
Eval.always(n == 0).flatMap { | ||
case true => Eval.now(true) | ||
case false => odd(n - 1) | ||
} | ||
|
||
def odd(n: Int): Eval[Boolean] = | ||
Eval.always(n == 0).flatMap { | ||
case true => Eval.now(false) | ||
case false => even(n - 1) | ||
} | ||
} | ||
|
||
|
||
MutualRecursion.odd(199999).value | ||
``` | ||
|
||
Because `Eval` guarantees stack-safety, we can chain a lot of computations together using `flatMap` without fear of blowing up the stack. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Might be worthy mentioning here that there are two advantages of
Later
overlazy val
Later
allows the thunk being GC'd after evaluation andLater
's synchronization lock locks itself while lazy vals lock their enclosing class. Discussed in detail here