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

Commit

Permalink
Move static doc from Arrow Docs to arrow-docs module
Browse files Browse the repository at this point in the history
  • Loading branch information
rachelcarmena committed Feb 27, 2020
1 parent f64314c commit df9b59b
Show file tree
Hide file tree
Showing 15 changed files with 2,659 additions and 0 deletions.
165 changes: 165 additions & 0 deletions arrow-docs/docs/docs/arrow-fx/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/docs/arrow-fx/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/docs/arrow-fx/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

0 comments on commit df9b59b

Please sign in to comment.