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

Move static doc from Arrow Docs to arrow-docs module #55

Merged
merged 2 commits into from
Feb 27, 2020
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
165 changes: 165 additions & 0 deletions arrow-docs/docs/async/README.md
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)
28 changes: 28 additions & 0 deletions arrow-docs/docs/effect/README.md
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>&mdash; 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)
69 changes: 69 additions & 0 deletions arrow-docs/docs/fiber/README.md
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 })
```
Loading