From 5f98f702de51347344c9e8dbc3d3b9ee8cf20314 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Fri, 11 Aug 2017 17:59:21 +0200 Subject: [PATCH 1/4] Add Eval documentation --- docs/src/main/tut/datatypes/eval.md | 108 ++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/src/main/tut/datatypes/eval.md diff --git a/docs/src/main/tut/datatypes/eval.md b/docs/src/main/tut/datatypes/eval.md new file mode 100644 index 0000000000..97987bb5a8 --- /dev/null +++ b/docs/src/main/tut/datatypes/eval.md @@ -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. From 15371a1e4b4529743f3a02784e584269224acd1e Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sun, 13 Aug 2017 16:16:27 +0200 Subject: [PATCH 2/4] Add advantages over lazy val --- docs/src/main/tut/datatypes/eval.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/src/main/tut/datatypes/eval.md b/docs/src/main/tut/datatypes/eval.md index 97987bb5a8..2bf343ce9e 100644 --- a/docs/src/main/tut/datatypes/eval.md +++ b/docs/src/main/tut/datatypes/eval.md @@ -61,6 +61,9 @@ 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 From 97b0262287ae23a852e27a6a43b71345adf7c32f Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Thu, 17 Aug 2017 15:53:36 +0200 Subject: [PATCH 3/4] Add docs for Eval.defer --- docs/src/main/tut/datatypes/eval.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/src/main/tut/datatypes/eval.md b/docs/src/main/tut/datatypes/eval.md index 2bf343ce9e..eaf8f67bd1 100644 --- a/docs/src/main/tut/datatypes/eval.md +++ b/docs/src/main/tut/datatypes/eval.md @@ -109,3 +109,6 @@ 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. From f84cfa84658b4688bdae0fca5b746b0e35092cef Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Fri, 25 Aug 2017 21:55:35 +0200 Subject: [PATCH 4/4] Add menu entry --- docs/src/main/resources/microsite/data/menu.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/src/main/resources/microsite/data/menu.yml b/docs/src/main/resources/microsite/data/menu.yml index 081dfffe2a..a53de55257 100644 --- a/docs/src/main/resources/microsite/data/menu.yml +++ b/docs/src/main/resources/microsite/data/menu.yml @@ -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