This repository has been archived by the owner on Feb 24, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move static doc from Arrow Docs to arrow-docs module
- Loading branch information
1 parent
f64314c
commit df9b59b
Showing
15 changed files
with
2,659 additions
and
0 deletions.
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,165 @@ | ||
--- | ||
layout: docs-fx | ||
title: Async | ||
permalink: /effects/async/ | ||
--- | ||
|
||
## Async | ||
|
||
|
||
|
||
|
||
Being able to run code in a different context of execution (i.e., thread) than the current one implies that, even if it's part of a sequence, the code will have to be asynchronous. | ||
Running asynchronous code always requires a callback after completion on error capable of returning to the current thread. | ||
|
||
The same way the typeclass [`Monad`]({{ '/arrow/typeclasses/monad' | relative_url }}) represents a sequence of events, and [`MonadError`]({{ '/arrow/typeclasses/monaderror' | relative_url }}) a sequence that can fail, the typeclass `Async` represents asynchronous code with a callback. | ||
Examples that can run code asynchronously are typically datatypes that can suspend effects and delay evaluation. | ||
|
||
```kotlin:ank | ||
import arrow.* | ||
import arrow.core.* | ||
import arrow.fx.* | ||
import arrow.fx.extensions.io.async.* | ||
IO.async() | ||
.async { callback: (Either<Throwable, Int>) -> Unit -> | ||
callback(1.right()) | ||
}.fix().attempt().unsafeRunSync() | ||
``` | ||
|
||
```kotlin:ank | ||
IO.async() | ||
.async { callback: (Either<Throwable, Int>) -> Unit -> | ||
callback(RuntimeException().left()) | ||
}.fix().attempt().unsafeRunSync() | ||
``` | ||
|
||
`Async` includes all combinators present in [`MonadDefer`]({{ '/effects/monaddefer/' | relative_url }}). | ||
|
||
### Main Combinators | ||
|
||
#### async | ||
|
||
Receives a function returning `Unit` with a callback as a parameter. | ||
The function is responsible for calling the callback once it obtains a result. | ||
The callback accepts `Either<Throwable, A>` as the return, where the left side of the [`Either`]({{ '/apidocs/arrow-core-data/arrow.core/-either/' | relative_url }}) represents an error in the execution, and the right side is the completion value of the operation. | ||
|
||
```kotlin | ||
IO.async() | ||
.async { callback: (Either<Throwable, Int>) -> Unit -> | ||
userFetcherWithCallback("1").startAsync({ user: User -> | ||
callback(user.left()) | ||
}, { error: Exception -> | ||
callback(error.right()) | ||
}) | ||
} | ||
``` | ||
|
||
```kotlin | ||
IO.async() | ||
.async { callback: (Either<Throwable, Int>) -> Unit -> | ||
userFromDatabaseObservable().subscribe({ user: User -> | ||
callback(user.left()) | ||
}, { error: Exception -> | ||
callback(error.right()) | ||
}) | ||
} | ||
``` | ||
|
||
#### continueOn | ||
|
||
It makes the rest of the operator chain execute on a separate `CoroutineContext`, effectively jumping threads if necessary. | ||
|
||
```kotlin | ||
IO.async().run { | ||
// In current thread | ||
just(createUserFromId(123)) | ||
.continueOn(CommonPool) | ||
// In CommonPool | ||
.flatMap { request(it) } | ||
.continueOn(Ui) | ||
// In Ui | ||
.flatMap { showResult(it) } | ||
} | ||
``` | ||
|
||
Behind the scenes, `continueOn()` starts a new coroutine and passes the rest of the chain as the block to execute. | ||
|
||
The function `continueOn()` is also available inside [`Monad Comprehensions`]({{ '/patterns/monad_comprehensions' | relative_url }}). | ||
|
||
#### effect | ||
|
||
Similar to `MonadDefer`'s `later`, this constructor takes a single suspended function and, optionally, the `CoroutineContext` it has to be run on. | ||
|
||
```kotlin | ||
IO.async().run { | ||
// In current thread | ||
effect(CommonPool) { | ||
// In CommonPool | ||
requestSuspend(createUserFromId(123)) | ||
} | ||
} | ||
``` | ||
|
||
#### invoke with CoroutineContext | ||
|
||
Similar to `MonadDefer`'s `later`, this constructor takes a single generator function and the `CoroutineContext` it has to be run on. | ||
|
||
```kotlin | ||
IO.async().run { | ||
// In current thread | ||
invoke(CommonPool) { | ||
// In CommonPool | ||
requestSync(createUserFromId(123)) | ||
} | ||
} | ||
``` | ||
|
||
#### defer with CoroutineContext | ||
|
||
Similar to `MonadDefer`'s `defer`, this constructor takes a single function returning a `Kind<F, A>` and the `CoroutineContext` it has to be run on. | ||
|
||
```kotlin | ||
IO.async().run { | ||
// In current thread | ||
defer(CommonPool) { | ||
// In CommonPool | ||
async { cb -> | ||
requestAsync(createUserFromId(123), cb) | ||
} | ||
} | ||
} | ||
``` | ||
|
||
#### never | ||
|
||
Creates an object using `async()` whose callback is never called. | ||
|
||
Depending on how the datatype is implemented, this may cause unexpected errors like awaiting forever for a result. | ||
|
||
Use with *SEVERE CAUTION*. | ||
|
||
```kotlin | ||
IO.async() | ||
.never() | ||
.unsafeRunSync() | ||
// ERROR!! The program blocks the current thread forever. | ||
``` | ||
|
||
> never() exists to test datatypes that can handle non-termination. | ||
For example, IO has unsafeRunTimed that runs never() safely. | ||
|
||
### Laws | ||
|
||
Arrow provides `AsyncLaws` in the form of test cases for internal verification of lawful instances and third party apps creating their own `Async` instances. | ||
|
||
### Data types | ||
|
||
```kotlin:ank:replace | ||
import arrow.reflect.* | ||
import arrow.fx.typeclasses.* | ||
TypeClass(Async::class).dtMarkdownList() | ||
``` | ||
|
||
ank_macro_hierarchy(arrow.fx.typeclasses.Async) |
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,28 @@ | ||
--- | ||
layout: docs-fx | ||
title: Effect | ||
permalink: /effects/effect/ | ||
--- | ||
|
||
## Effect | ||
|
||
|
||
|
||
|
||
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">But can I use callback? I love callbacks. Christmas tree style. with the lights and all....</p>— Hadi Hariri (@hhariri) <a href="https://twitter.com/hhariri/status/986652337543491586?ref_src=twsrc%5Etfw">April 18, 2018</a></blockquote> | ||
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> | ||
|
||
If you want to use callbacks or run suspended datatypes, then `Effect` is the typeclass to use. It contains a single function `runAsync` that takes a callback and returns a new instance of the datatype. The operation will not yield a result immediately; to start running the suspended computation, you have to evaluate that new instance using its own start operator: `unsafeRunAsync` or `unsafeRunSync` for `IO`, `subscribe` or `blocking` for `Observable`, and `await` or `runBlocking` for `Deferred`. | ||
|
||
TODO. Meanwhile you can find a short description in the [intro to typeclasses]({{ '/typeclasses/intro/' | relative_url }}). | ||
|
||
### Data types | ||
|
||
```kotlin:ank:replace | ||
import arrow.reflect.* | ||
import arrow.fx.typeclasses.* | ||
TypeClass(Effect::class).dtMarkdownList() | ||
``` | ||
|
||
ank_macro_hierarchy(arrow.fx.typeclasses.Effect) |
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,69 @@ | ||
--- | ||
layout: docs-fx | ||
title: Fiber | ||
permalink: /effects/fiber/ | ||
--- | ||
|
||
## Fiber | ||
|
||
|
||
A `Fiber` is a concurrency primitive for describing parallel operations or multi-tasking. | ||
Concurrently started tasks can either be joined or canceled, and these are the only two operators available on `Fiber`. | ||
|
||
Using `Fiber`, we can describe parallel operations such as `parallelMap` relatively easily. | ||
**Note** the operation written below does not support proper cancellation. | ||
When the resulting `IO` is canceled, it does not propagate this cancellation back to the underlying `IO`. | ||
|
||
```kotlin:ank | ||
import arrow.fx.* | ||
import kotlinx.coroutines.Dispatchers.Default | ||
import arrow.fx.extensions.fx | ||
import arrow.fx.typeclasses.Fiber | ||
import arrow.fx.IO | ||
fun <A, B, C> parallelMap(first: IO<A>, | ||
second: IO<B>, | ||
f: (A, B) -> C): IO<C> = | ||
IO.fx { | ||
val (fiberOne: Fiber<ForIO, A>) = first.fork(Default) | ||
val (fiberTwo: Fiber<ForIO, B>) = second.fork(Default) | ||
f(!fiberOne.join(), !fiberTwo.join()) | ||
} | ||
val first = IO<Unit> { Thread.sleep(5000) }.map { | ||
println("Hi, I am first") | ||
1 | ||
} | ||
val second = IO<Unit> { Thread.sleep(5000) }.map { | ||
println("Hi, I am second") | ||
2 | ||
} | ||
``` | ||
|
||
```kotlin | ||
parallelMap(first, second, Int::plus).await() | ||
|
||
//Hi, I am second | ||
//Hi, I am first | ||
//3 | ||
``` | ||
|
||
We could fix this snippet to support proper cancellation by using `bracket` instead of `flatMap`, | ||
which allows us to register an operation to run on cancellation, error, or completion. | ||
|
||
```kotlin:ank | ||
import arrow.fx.extensions.io.monad.flatMap | ||
import arrow.fx.extensions.io.monad.map | ||
fun <A, B, C> parallelMap2(first: IO<A>, | ||
second: IO<B>, | ||
f: (A, B) -> C): IO<C> = | ||
first.fork(Default).bracket(use = { (joinA, _) -> | ||
second.fork(Default).bracket(use = { (joinB, _) -> | ||
joinA.flatMap { a -> | ||
joinB.map { b -> f(a, b) } | ||
} | ||
}, release = { (_, cancelB) -> cancelB }) | ||
}, release = { (_, cancelA) -> cancelA }) | ||
``` |
Oops, something went wrong.