diff --git a/modules/core/arrow-core-data/src/main/kotlin/arrow/core/Eval.kt b/modules/core/arrow-core-data/src/main/kotlin/arrow/core/Eval.kt index 2600e89759f..25d34050b5c 100644 --- a/modules/core/arrow-core-data/src/main/kotlin/arrow/core/Eval.kt +++ b/modules/core/arrow-core-data/src/main/kotlin/arrow/core/Eval.kt @@ -29,6 +29,32 @@ fun EvalOf.value(): A = this.fix().value() * Eval instances whose computation involves calling .value on another * Eval instance -- this can defeat the trampolining and lead to stack * overflows. + * + * Example of stack safety: + * + * ```kotlin:ank:playground + * import arrow.core.Eval + * + * //sampleStart + * fun even(n: Int): Eval = + * Eval.always { n == 0 }.flatMap { + * if(it == true) Eval.now(true) + * else odd(n - 1) + * } + * + * fun odd(n: Int): Eval = + * Eval.always { n == 0 }.flatMap { + * if(it == true) Eval.now(false) + * else even(n - 1) + * } + * + * // if not wrapped in eval this type of computation would blow the stack and result in a StackOverflowError + * fun main() { + * println(odd(100000).value()) + * } + * //sampleEnd + * ``` + * */ @higherkind sealed class Eval : EvalOf { @@ -45,10 +71,64 @@ sealed class Eval : EvalOf { fun just(a: A): Eval = now(a) + /** + * Creates an Eval instance from an already constructed value but still defers evaluation when chaining expressions with `map` and `flatMap` + * + * @param a is an already computed value of type [A] + * + * ```kotlin:ank:playground + * import arrow.core.* + * + * fun main() { + * //sampleStart + * val eager = Eval.now(1).map { it + 1 } + * println(eager.value()) + * //sampleEnd + * } + * ``` + * + * It will return 2. + */ fun now(a: A) = Now(a) + /** + * Creates an Eval instance from a function deferring it's evaluation until `.value()` is invoked memoizing the computed value. + * + * @param f is a function or computation that will be called only once when `.value()` is invoked for the first time. + * + * ```kotlin:ank:playground + * import arrow.core.* + * + * fun main() { + * //sampleStart + * val lazyEvaled = Eval.later { "expensive computation" } + * println(lazyEvaled.value()) + * //sampleEnd + * } + * ``` + * + * "expensive computation" is only computed once since the results are memoized and multiple calls to `value()` will just return the cached value. + */ fun later(f: () -> A) = Later(f) + /** + * Creates an Eval instance from a function deferring it's evaluation until `.value()` is invoked recomputing each time `.value()` is invoked. + * + * @param f is a function or computation that will be called every time `.value()` is invoked. + * + * ```kotlin:ank:playground + * import arrow.core.* + * + * fun main() { + * //sampleStart + * val alwaysEvaled = Eval.always { "expensive computation" } + * println(alwaysEvaled.value()) + * //sampleEnd + * } + * ``` + * + * "expensive computation" is computed every time `value()` is invoked. + */ fun always(f: () -> A) = Always(f) fun defer(f: () -> Eval): Eval = Defer(f) diff --git a/modules/docs/arrow-docs/docs/_data/sidebar.yml b/modules/docs/arrow-docs/docs/_data/sidebar.yml index 53aeb14ea43..f9519e7ed34 100644 --- a/modules/docs/arrow-docs/docs/_data/sidebar.yml +++ b/modules/docs/arrow-docs/docs/_data/sidebar.yml @@ -200,7 +200,7 @@ options: url: /docs/arrow/data/coproduct/ - title: Eval - url: /docs/arrow/core/eval/ + url: /docs/apidocs/arrow-core-data/arrow.core/-eval/ - title: OptionT url: /docs/arrow/data/optiont/ diff --git a/modules/docs/arrow-docs/docs/docs/arrow/core/eval/README.md b/modules/docs/arrow-docs/docs/docs/arrow/core/eval/README.md deleted file mode 100644 index 36558d1c0c7..00000000000 --- a/modules/docs/arrow-docs/docs/docs/arrow/core/eval/README.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -layout: docs -title: Eval -permalink: /docs/arrow/core/eval/ -redirect_from: - - /docs/datatypes/eval/ -video: FcaaTJhCEcw ---- - -## Eval - -{:.beginner} -beginner - -Eval is a monad that allows us to control evaluation and chain lazy operations that are not executed until `value()` is invoked. - -Eval includes internally trampolining facilities which allows us to chain computations without fear of blowing up the stack. -There are two different factors that play into evaluation: memoization and laziness. - -Eval supports memoization, eager and lazy evaluation strategies - -### now - -`Eval#now` creates an Eval instance from an already constructed value but still defers evaluation when chaining expressions with `map` and `flatMap` - -```kotlin:ank -import arrow.* -import arrow.core.* - -val eager = Eval.now(1).map { it + 1 } -eager.value() -``` - -### later - -`Eval#later` creates an Eval instance from a function deferring it's evaluation until `.value()` is invoked memoizing the computed value. - -```kotlin:ank -val lazyEvaled = Eval.later { "expensive computation" } -lazyEvaled.value() -``` - -`"expensive computation"` is only computed once since the results are memoized and multiple calls to `value()` will just return the cached value. - -### always - -`Eval#always` creates an Eval instance from a function deferring it's evaluation until `.value()` is invoked recomputing each time `.value()` is invoked. - -```kotlin:ank -val alwaysEvaled = Eval.always { "expensive computation" } -alwaysEvaled.value() -``` - -### Stack safety - -`Eval` empowers stack safe programs by chaining lazy computations - -```kotlin:ank -fun even(n: Int): Eval = - Eval.always { n == 0 }.flatMap { - if(it == true) Eval.now(true) - else odd(n - 1) - } - -fun odd(n: Int): Eval = - Eval.always { n == 0 }.flatMap { - if(it == true) Eval.now(false) - else even(n - 1) - } - -// if not wrapped in eval this type of computation would blow the stack and result in a StackOverflowError -odd(100000).value() -``` - -### Supported type classes - -```kotlin:ank:replace -import arrow.reflect.* -import arrow.data.* -import arrow.core.* - -DataType(Eval::class).tcMarkdownList() -``` - -## Credits - -Contents partially adapted from [Cats Eval](https://typelevel.org/cats/datatypes/eval.html) diff --git a/modules/docs/arrow-docs/docs/docs/arrow/typeclasses/foldable/README.md b/modules/docs/arrow-docs/docs/docs/arrow/typeclasses/foldable/README.md index f3dea909bb6..0d8cacd401e 100644 --- a/modules/docs/arrow-docs/docs/docs/arrow/typeclasses/foldable/README.md +++ b/modules/docs/arrow-docs/docs/docs/arrow/typeclasses/foldable/README.md @@ -67,7 +67,7 @@ Right associative lazy fold on `F` using the provided function. This method evaluates `lb` lazily, and returns a lazy value to support laziness in a stack-safe way avoiding StackOverflows. -For more detailed information about how this method works see the documentation for [`Eval`]({{ '/docs/arrow/core/eval' | relative_url }}). +For more detailed information about how this method works see the documentation for [`Eval`]({{ '/docs/apidocs/arrow-core-data/arrow.core/-eval' | relative_url }}). ```kotlin:ank:silent fun concatenateStringFromRight(strKind: Kind, FO: Foldable): String = diff --git a/modules/docs/arrow-docs/docs/docs/arrow/typeclasses/monad/README.md b/modules/docs/arrow-docs/docs/docs/arrow/typeclasses/monad/README.md index 6356020fe20..a79756ff231 100644 --- a/modules/docs/arrow-docs/docs/docs/arrow/typeclasses/monad/README.md +++ b/modules/docs/arrow-docs/docs/docs/arrow/typeclasses/monad/README.md @@ -85,11 +85,11 @@ Some(5).mproduct { #### followedBy/followedByEval Executes sequentially two elements that are independent from one another. -The [`Eval`]({{ '/docs/arrow/core/eval' | relative_url }}) variant allows you to pass lazily calculated values. +The [`Eval`]({{ '/docs/apidocs/arrow-core-data/arrow.core/-eval' | relative_url }}) variant allows you to pass lazily calculated values. ```kotlin:ank import arrow.core.extensions.option.monad.followedBy - + Some(1).followedBy(Some(2)) ``` @@ -108,7 +108,7 @@ IO.just(1).effectM(::logValue).fix().unsafeRunSync() #### forEffect/forEffectEval Executes sequentially two elements that are independent from one another, ignoring the value of the second one. -The [`Eval`]({{ '/docs/arrow/core/eval' | relative_url }}) variant allows you to pass lazily calculated values. +The [`Eval`]({{ '/docs/apidocs/arrow-core-data/arrow.core/-eval' | relative_url }}) variant allows you to pass lazily calculated values. ```kotlin:ank import arrow.core.extensions.option.monad.* diff --git a/modules/docs/arrow-docs/docs/docs/datatypes/intro/README.md b/modules/docs/arrow-docs/docs/docs/datatypes/intro/README.md index 1c1a1575beb..f34d58137b8 100644 --- a/modules/docs/arrow-docs/docs/docs/datatypes/intro/README.md +++ b/modules/docs/arrow-docs/docs/docs/datatypes/intro/README.md @@ -51,7 +51,7 @@ so they are always required. - [`Either`]({{ '/docs/arrow/core/either/' | relative_url }}) - an if/else branch in execution -- [`Eval`]({{ '/docs/arrow/core/eval/' | relative_url }}) - lazy evaluation of functions with stack safety and memoization +- [`Eval`]({{ '/docs/apidocs/arrow-core-data/arrow.core/-eval' | relative_url }}) - lazy evaluation of functions with stack safety and memoization - `TupleN` - a heterogeneous grouping of 2-9 values without creating a named class diff --git a/modules/docs/arrow-docs/docs/docs/quickstart/libraries/README.md b/modules/docs/arrow-docs/docs/docs/quickstart/libraries/README.md index 1018fbccda5..ce2db147f15 100644 --- a/modules/docs/arrow-docs/docs/docs/quickstart/libraries/README.md +++ b/modules/docs/arrow-docs/docs/docs/quickstart/libraries/README.md @@ -21,7 +21,7 @@ beginner The smallest set of [datatypes]({{ '/docs/datatypes/intro/' | relative_url }}) necessary to start in FP, and that other libraries can build upon. The focus here is on API design and abstracting small code patterns. -Datatypes: [`Either`]({{ '/docs/arrow/core/either/' | relative_url }}), [`Option`]({{ '/docs/arrow/core/option/' | relative_url }}), [`Try`]({{ '/docs/arrow/core/try/' | relative_url }}), [`Eval`]({{ '/docs/arrow/core/eval/' | relative_url }}), [`Id`]({{ '/docs/arrow/core/id/' | relative_url }}), `TupleN`, `Function0`, `Function1`, `FunctionK` +Datatypes: [`Either`]({{ '/docs/arrow/core/either/' | relative_url }}), [`Option`]({{ '/docs/arrow/core/option/' | relative_url }}), [`Try`]({{ '/docs/arrow/core/try/' | relative_url }}), [`Eval`]({{ '/docs/apidocs/arrow-core-data/arrow.core/-eval' | relative_url }}), [`Id`]({{ '/docs/arrow/core/id/' | relative_url }}), `TupleN`, `Function0`, `Function1`, `FunctionK` ### arrow-syntax