Skip to content
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 4 commits into from
Sep 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/src/main/resources/microsite/data/menu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ options:
url: datatypes/either.html
menu_type: data

- title: Eval
url: datatypes/eval.html
menu_type: data

- title: FreeApplicatives
url: datatypes/freeapplicative.html
menu_type: data
Expand Down
114 changes: 114 additions & 0 deletions docs/src/main/tut/datatypes/eval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
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`:
Copy link
Contributor

@kailuowang kailuowang Aug 11, 2017

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 over lazy val

  1. Later allows the thunk being GC'd after evaluation and
  2. Later's synchronization lock locks itself while lazy vals lock their enclosing class. Discussed in detail here


```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.later` is different to using a `lazy val` in a few different ways.
First, it allows the runtime to perform garbage collection of the thunk after evaluation, leading to more memory being freed earlier.
Secondly, when `lazy val`s are evaluated, in order to preserve thread-safety, the Scala compiler will lock the whole surrounding class, whereas `Eval` will only lock itself.

#### 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.

You can also use `Eval.defer` to defer any computation that will return an `Eval[A]`.
This is useful, because nesting a call to `.value` inside any of the `Eval` creation methods can be unsafe.