Skip to content
This repository has been archived by the owner on Feb 24, 2021. It is now read-only.

Deprecate IO (core docs) #275

Merged
merged 10 commits into from Dec 22, 2020
4 changes: 2 additions & 2 deletions arrow-core-data/src/main/kotlin/arrow/core/AndThen.kt
Expand Up @@ -73,7 +73,7 @@ sealed class AndThen<A, B> : (A) -> B, AndThenOf<A, B> {
*/
fun <X> andThen(g: (B) -> X): AndThen<A, X> =
when (this) {
// Fusing calls up to a certain threshold, using the fusion technique implemented for `IO#map`
// Fusing calls up to a certain threshold
is Single ->
if (index != maxStackDepthSize) Single(f andThen g, index + 1)
else andThenF(AndThen(g))
Expand Down Expand Up @@ -105,7 +105,7 @@ sealed class AndThen<A, B> : (A) -> B, AndThenOf<A, B> {
*/
infix fun <C> compose(g: (C) -> A): AndThen<C, B> =
when (this) {
// Fusing calls up to a certain threshold, using the fusion technique implemented for `IO#map`
// Fusing calls up to a certain threshold
is Single ->
if (index != maxStackDepthSize) Single(f compose g, index + 1)
else composeF(AndThen(g))
Expand Down
3 changes: 1 addition & 2 deletions arrow-core-data/src/main/kotlin/arrow/typeclasses/Monad.kt
Expand Up @@ -17,8 +17,7 @@ import kotlin.coroutines.startCoroutine
* Given a type constructor [F] with a value of [A] we can compose multiple operations of type
* `Kind<F, ?>` where `?` denotes a value being transformed.
*
* This is true for all type constructors that can support the [Monad] type class including and not limited to
* [IO], [ObservableK], [Option], [Either], [List] ...
* This is true for all type constructors that can support the [Monad] type class including and not limited to [Option], [Either], [List] ...
*
* [The Monad Tutorial](https://arrow-kt.io/docs/patterns/monads/)
*
Expand Down
95 changes: 42 additions & 53 deletions arrow-core-data/src/main/kotlin/arrow/typeclasses/Traverse.kt
Expand Up @@ -20,29 +20,32 @@ import arrow.core.ValidatedNel
*
* These behaviors tend to show up in functions working on a single piece of data - for instance parsing a single [String] into an [Int], validating a login, or asynchronously fetching website information for a user.
*
* ```kotlin
* import arrow.fx.IO
* ```kotlin:ank
* import arrow.core.Either
* import arrow.core.Right
*
* interface Profile
* interface User
* object Profile
* object NotFound
* object User
*
* fun userInfo(u: User): IO<Profile>
* fun userInfo(user: User): Either<NotFound, Profile> =
* Right(Profile)
* ```
*
* Each function asks only for the data it needs; in the case of `userInfo`, a single `User`. Indeed, we could write one that takes a `List<User>` and fetches profile for all of them, but it would be a bit strange. If we just wanted to fetch the profile of only one user, we would either have to wrap it in a [List] or write a separate function that takes in a single user, nonetheless. More fundamentally, functional programming is about building lots of small, independent pieces and composing them to make larger and larger pieces - does it hold in this case?
*
* Given just `(User) -> IO<Profile>`, what should we do if we want to fetch profiles for a `List<User>`? We could try familiar combinators like map.
* Given just `(User) -> Either<NotFound, Profile>`, what should we do if we want to fetch profiles for a `List<User>`? We could try familiar combinators like map.
*
* ```kotlin
* fun profilesFor(users: List<User>): List<IO<Profile>> =
* ```kotlin:ank
* fun profilesFor(users: List<User>): List<Either<NotFound, Profile>> =
* users.map(::userInfo)
* ```
*
* Note the return type `List<IO<Profile>>`. This makes sense given the type signatures, but seems unwieldy. We now have a list of asynchronous values, and to work with those values we must then use the combinators on `IO` for every single one. It would be nicer instead if we could get the aggregate result in a single `IO`, say a `IO<List<Profile>>`.
* Note the return type `List<Either<NotFound, Profile>>`. This makes sense given the type signatures, but seems unwieldy. We now have a list of result values, and to work with those values we must then use the combinators on `Either` for every single one. It would be nicer instead if we could get the aggregate result in a single `Either`, say a `Either<NotFound, List<Profile>>`.
*
* ### Sequencing
*
* Similar to the latter, you may find yourself with a collection of data, each of which is already in an data type, for instance a `List<Option<A>>` or `List<IO<Profile>>`. To make this easier to work with, you want a `Option<List<A>>` or `IO<List<Profile>>`. Given `Option` and `IO` have an [Applicative] instance, we can [sequence] the list to reverse the types.
* Similar to the latter, you may find yourself with a collection of data, each of which is already in an data type, for instance a `List<Option<A>>` or `List<Either<NotFound, Profile>>`. To make this easier to work with, you want a `Option<List<A>>` or `Either<NotFound, List<Profile>>`. Given `Option` and `Either` have access to [traverse] and [sequence] we can reverse the types.
*
* ```kotlin:ank:playground
* import arrow.core.Option
Expand Down Expand Up @@ -112,7 +115,7 @@ import arrow.core.ValidatedNel
* }
* ```
*
* In our above example, `F` is `List`, and `G` is `Option` or `IO`. For the profile example, traverse says given a `List<User>` and a function `(User) -> IO<Profile>`, it can give you a `IO<List<Profile>>`.
* In our above example, `F` is `List`, and `G` is `Option` or `Either`. For the profile example, traverse says given a `List<User>` and a function `(User) -> Either<NotFound, Profile>`, it can give you a `Either<NotFound, List<Profile>>`.
*
* Abstracting away the `G` (still imagining `F` to be `List`), [traverse] says given a collection of data, and a function that takes a piece of data and returns an value `B` wrapped in a container `G`, it will traverse the collection, applying the function and aggregating the values (in a `List`) as it goes.
*
Expand All @@ -124,55 +127,46 @@ import arrow.core.ValidatedNel
* map(::f).sequence(AP) == traverse(AP, ::f)
* ```
*
* where AP stands for an `Applicative<G>`, which is in prior snippets `Applicative<ForOption>` or `Applicative<ForIO>`.
* where AP stands for an `Applicative<G>`, which is in prior snippets `Applicative<ForOption>` or `Applicative<EitherPartialOf<E>>`.
*
* ### Sequencing effects
*
* Sometimes our effectful functions return a [Unit] value in cases where there is no interesting value to return (e.g. writing to some sort of store).
*
* ```kotlin:ank
* import arrow.fx.IO
*
* interface Data
*
* fun writeToStore(data: Data): IO<Unit> = TODO("")
* fun writeToStore(data: Data): Either<NotFound, Unit> =
* Right(Unit)
* ```
*
* If we traverse using this, we end up with a funny type.
*
* ```kotlin:ank
* import arrow.core.ListK
* import arrow.fx.IO
* import arrow.fx.extensions.io.applicative.applicative
* import arrow.fx.fix
* import arrow.core.extensions.either.applicative.applicative
* import arrow.core.fix
*
* interface Data
*
* fun writeToStore(data: Data): IO<Unit> = TODO("")
* //sampleStart
* fun writeManyToStore(data: ListK<Data>): IO<ListK<Unit>> =
* data.traverse(IO.applicative()) { writeToStore(it) }.fix()
* //sampleEnd
* fun writeToStore(data: Data): Either<NotFound, Unit> = TODO("")
*
* fun writeManyToStore(data: ListK<Data>): Either<NotFound, ListK<Unit>> =
* data.traverse(Either.applicative()) { writeToStore(it) }.fix()
* ```
*
* We end up with a `IO<ListK<Unit>>`! A `ListK<Unit>` is not of any use to us, and communicates the same amount of information as a single [Unit] does.
* We end up with a `Either<NotFound, List<Unit>>`! A `List<Unit>` is not of any use to us, and communicates the same amount of information as a single [Unit] does.
*
* Traversing solely for the sake of the effects (ignoring any values that may be produced, [Unit] or otherwise) is common, so [Foldable] (superclass of [Traverse]) provides [traverse_] and [sequence_] methods that do the same thing as [traverse] and [sequence] but ignore any value produced along the way, returning [Unit] at the end.
*
* ```kotlin:ank
* import arrow.core.ListK
* import arrow.core.extensions.list.foldable.traverse_
* import arrow.fx.IO
* import arrow.fx.extensions.io.applicative.applicative
* import arrow.fx.fix
*
* interface Data
* fun writeToStore(data: Data): Either<NotFound, Unit> = TODO("")
*
* fun writeToStore(data: Data): IO<Unit> = TODO("")
* //sampleStart
* fun writeManyToStore(data: ListK<Data>): IO<Unit> =
* data.traverse_(IO.applicative()) { writeToStore(it) }.fix()
* //sampleEnd
* fun writeManyToStore(data: List<Data>): Either<NotFound, Unit> =
* data.traverse_(Either.applicative()) { writeToStore(it) }.fix()
* ```
*
* ```kotlin:ank:playground
Expand Down Expand Up @@ -340,41 +334,36 @@ import arrow.core.ValidatedNel
*
* Notice that in the [Either] case, should any string fail to parse the entire [traverse] is considered a failure. Moreover, once it hits its first bad parse, it will not attempt to parse any others down the line (similar behavior would be found with using `Option`). Contrast this with `Validated` where even if one bad parse is hit, it will continue trying to parse the others, accumulating any and all errors as it goes. The behavior of [traverse] is closely tied with the [Applicative] behavior of the data type, where computations are run in isolation.
*
* Going back to our `IO` example from the beginning with concurrency in mind, we can get an [Applicative] instance for `IO`, that traverses concurrently, by using `parApplicative`. Contrary to `IO.applicative()`, which runs in sequence.
*
* It is worth mentioning that `parApplicative` does not implement `lazyAp` in contrast to `IO.applicative()`, which means `parApplicative` creates all IO's upfront and executes them in parallel. More importantly, `parApplicative` breaks monad-applicative-consistency laws by design. The aforementioned law holds in the case of `IO.applicative()`, where an effect only runs , when the previous one is successful - hence the monadic nature.
*
* Continuing with the example, we traverse a `List<A>` with its [Traverse] instance `ListK.traverse()` and a function`(A) -> IO<B>`, we can imagine the traversal as a scatter-gather. Each `A` creates a concurrent computation that will produce a `B` (the scatter), and as the `IO` operations completes they will be gathered back into a `List`.
*
* Ultimately, we utilize `parTraverse` that calls traverse with `parApplicative` in an concurrent environment [F] - in the following example `IO`:
* Continuing with the example, we traverse a `List<A>` with its [traverse] and a function `(A) -> Either<NotFound, B>`, we can imagine the traversal as a scatter-gather. Each `A` creates a concurrent computation that will produce a `B` (the scatter), and as the suspended `Either` operations completes they will be gathered back into a `List`.
*
* ```kotlin:ank:playground
* import arrow.fx.IO
* import arrow.fx.extensions.fx
* import arrow.fx.extensions.io.concurrent.parTraverse
* import arrow.fx.extensions.io.unsafeRun.runBlocking
* import arrow.unsafe
* import arrow.core.computations.either
* import arrow.fx.coroutines.parTraverse
* import arrow.core.Right
*
* interface Profile
* interface User
* //sampleStart
* data class DummyUser(val name: String) : User
* data class DummyProfile(val u: User) : Profile
*
* fun userInfo(u: User): IO<Profile> =
* IO { DummyProfile(u) } // this can be a call to the DB
* suspend fun userInfo(u: User): Either<NotFound, Profile> =
* Right(DummyProfile(u)) // this can be a call to the DB
*
* fun List<User>.processLogin(): IO<List<Profile>> =
* parTraverse { userInfo(it) }
* suspend fun List<User>.processLogin(): Either<NotFound, List<Profile>> =
* either {
* parTraverse { userInfo(it)() }
* }
*
* fun program(): IO<Unit> = IO.fx {
* suspend fun program(): Unit {
* val list = listOf(DummyUser("Micheal"), DummyUser("Juan"), DummyUser("T'Challa"))
* .processLogin()()
* .processLogin()
raulraja marked this conversation as resolved.
Show resolved Hide resolved
* println(list)
* }
*
* //sampleEnd
* fun main() {
* program().unsafeRunSync()
* suspend fun main() {
* program()
* }
* ```
*
Expand Down
68 changes: 53 additions & 15 deletions arrow-docs/docs/arrow/typeclasses/applicativeerror/README.md
Expand Up @@ -33,13 +33,6 @@ import arrow.core.extensions.either.applicativeError.*
Either.applicativeError<Throwable>().raiseError<Int>(RuntimeException("Paco"))
```

```kotlin:ank
import arrow.fx.*
import arrow.fx.extensions.io.applicativeError.*

IO.applicativeError().raiseError<Int>(RuntimeException("Paco"))
```

#### Kind<F, A>#handleErrorWith

This method requires a function that creates a new datatype from an error, `(E) -> Kind<F, A>`. This function is used as a catch + recover clause for the current instance, allowing it to return a new computation after a failure.
Expand Down Expand Up @@ -76,12 +69,34 @@ failure.handleError { t -> 0 }

Maps the current content of the datatype to an [`Either<E, A>`]({{ '/apidocs/arrow-core-data/arrow.core/-either/' | relative_url }}), recovering from any previous error state.

```kotlin:ank
IO { "3".toInt() }.attempt()
```kotlin:ank:playground
import arrow.core.Either
import arrow.core.Validated
import arrow.core.valid

//sampleStart
val validation: Validated<String, Int> = 3.valid()
val result = validation.attempt()
//sampleEnd

suspend fun main() {
println(result)
}
```

```kotlin:ank
IO { "nope".toInt() }.attempt()
```kotlin:ank:playground
import arrow.core.Either
import arrow.core.Validated
import arrow.core.invalid

//sampleStart
val validation: Validated<String, Int> = "nope".invalid()
val result = validation.attempt()
//sampleEnd

suspend fun main() {
println(result)
}
```

#### fromEither/fromOption
Expand All @@ -96,12 +111,35 @@ Either.applicativeError<Throwable>().run { Some(1).fromOption { RuntimeException

In the case of `fromEither()`, converting from the error type of the `Either<EE, A>` to the type of the ApplicativeError<F, E> is required.

```kotlin:ank
IO.applicativeError().run { Either.Right(1).fromEither { it } }
```kotlin:ank:playground
import arrow.core.Either
import arrow.core.Right
import arrow.core.Validated

//sampleStart
val e: Either<String, Int> = Right(1)
val result = Validated.fromEither(e)
//sampleEnd

suspend fun main() {
println(result)
}
```

```kotlin:ank
IO.applicativeError().run { Either.Left(RuntimeException("Boom")).fromEither { it } }

```kotlin:ank:playground
import arrow.core.Either
import arrow.core.Left
import arrow.core.Validated

//sampleStart
val e: Either<String, Int> = Left("oops")
val result = Validated.fromEither(e)
//sampleEnd

suspend fun main() {
println(result)
}
```

#### catch
Expand Down
1 change: 0 additions & 1 deletion arrow-docs/docs/arrow/typeclasses/const/README.md
Expand Up @@ -18,4 +18,3 @@ import arrow.core.Const
DataType(Const::class).tcMarkdownList()
```

TODO. Meanwhile you can find a short description in the [intro to datatypes]({{ '/datatypes/intro/' | relative_url }}).
8 changes: 4 additions & 4 deletions arrow-docs/docs/arrow/typeclasses/monad/README.md
Expand Up @@ -52,7 +52,7 @@ Right(1).flatMap { _ ->
```

Note that, depending on the implementation of `Kind<F, A>`, this chaining function may be executed immediately, i.e., for `Option` or `Either`;
or lazily, i.e., `IO` or `ObservableK`.
or lazily, i.e., `suspend` or `Flow`.

#### Kind<F, Kind<F, A>>#flatten

Expand Down Expand Up @@ -96,11 +96,11 @@ Some(1).followedBy(Some(2))
Sequentially executes two elements and ignores the result of the second. This is useful for effects like logging.

```kotlin:ank
import arrow.fx.extensions.io.monad.*
import arrow.core.extensions.option.monad.flatTap

fun logValue(i: Int): IO<Unit> = IO { /* println(i) */ }
fun logValue(i: Int): Option<Unit> = Some(println(i))

IO.just(1).flatTap(::logValue).fix().unsafeRunSync()
Some(1).flatTap(::logValue)
```

#### productL/productLEval (formerly ~~forEffect~~/~~forEffectEval~~)
Expand Down
13 changes: 2 additions & 11 deletions arrow-docs/docs/arrow/typeclasses/monaderror/README.md
Expand Up @@ -29,21 +29,12 @@ import arrow.*
import arrow.core.*
import arrow.core.extensions.either.applicativeError.*

val eitherResult: Either<Throwable, Int> =
RuntimeException("BOOM!").raiseError()
val eitherResult: Either<String, Int> =
"BOOM!".raiseError()

eitherResult
```

```kotlin:ank
import arrow.fx.*
import arrow.fx.extensions.io.applicativeError.*

val ioResult: IO<Int> =
RuntimeException("BOOM!").raiseError()

ioResult.attempt().unsafeRunSync()
```

#### Kind<F, A>.ensure

Expand Down