Skip to content

Commit

Permalink
Documentation: Revise Getting Started, Add Execution, Creating and Ca…
Browse files Browse the repository at this point in the history
…ts-Effect Integration sections (#141)
  • Loading branch information
Avasil authored Jun 27, 2020
1 parent 813992e commit fd63561
Show file tree
Hide file tree
Showing 11 changed files with 745 additions and 103 deletions.
8 changes: 7 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ lazy val benchmarks = project.in(file("benchmarks"))
.settings(crossSettings)
.settings(
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % "1.0.0-RC18-2",
"dev.zio" %% "zio" % "1.0.0-RC21",
"io.monix" %% "monix-eval" % monixVersion
))

Expand All @@ -56,6 +56,12 @@ lazy val docs = project
)
.dependsOn(coreJVM)
.enablePlugins(DocusaurusPlugin, MdocPlugin, ScalaUnidocPlugin)
.settings(
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % "1.0.0-RC21",
"dev.zio" %% "zio-interop-cats" % "2.1.3.0-RC16",
"io.monix" %% "monix-eval" % monixVersion
))

lazy val mdocSettings = Seq(
scalacOptions --= Seq("-Xfatal-warnings", "-Ywarn-unused"),
Expand Down
133 changes: 133 additions & 0 deletions docs/catseffect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
id: cats-effect
title: Cats-Effect Integration
---

`BIO` provides [Cats-Effect](https://github.com/typelevel/cats-effect/) integration out of the box.
In practice, it means that integration with Typelevel libraries, such as [http4s](https://github.com/http4s/http4s), or [doobie](https://github.com/tpolecat/doobie) should work without much hassle.

## Getting instances in scope

All Cats instances up until [Effect](https://typelevel.org/cats-effect/typeclasses/effect.html) and [ConcurrentEffect](https://typelevel.org/cats-effect/typeclasses/concurrent-effect.html) (excluded) are available automatically, without any imports.

```scala mdoc:silent:reset
import cats.syntax.parallel._
import monix.bio.BIO

val taskA = BIO(20)
val taskB = BIO(22)

// evaluates taskA and taskB in parallel, then sums the results
val taskAplusB = (taskA, taskB).parMapN(_ + _)
```

`ConcurrentEffect` and `Effect` can be derived if there is `Scheduler` in scope.

### Sync and above

Infamous [Sync type class](https://typelevel.org/cats-effect/typeclasses/sync.html) extends `Bracket[F, Throwable]`.
`Throwable` is the error type and as an unfortunate consequence - any type class from `Sync` and above will only work with `BIO[Throwable, A]`.

For instance, let's say we want to use [monix.catnap.ConcurrentQueue](https://monix.io/api/current/monix/catnap/ConcurrentQueue.html)
which exposes an interface built on Cats-Effect type classes so it can be used with any effect:

```scala mdoc:silent:reset
import monix.bio.{BIO, Task}
import monix.catnap.ConcurrentQueue

val queueExample: BIO[Throwable, String] = for {
queue <- ConcurrentQueue[Task].bounded[String](10)
_ <- queue.offer("Message")
msg <- queue.poll
} yield msg
```

The `bounded` constructor requires `Concurrent[F]` and `ContextShift[F]` in scope.
Both requirements are automatically derived by `BIO`, but `Concurrent` extends `Sync`, so we need to settle on `Throwable` error type.
Since our `F` is `BIO[Throwable, *]`, all operations on `ConcurrentQueue` will return `BIO[Throwable, *]`.

A workaround is to use `hideErrors` because these methods don't throw any errors, and even if they did - how would we handle them?

```scala mdoc:silent:reset
import monix.bio.{Task, UIO}
import monix.catnap.ConcurrentQueue

val queueExample: UIO[String] = (for {
queue <- ConcurrentQueue[Task].bounded[String](10)
_ <- queue.offer("Message")
msg <- queue.poll
} yield msg).hideErrors
```

If typed errors prove to be a great idea in the long term, and not just a temporary fashion, new editions of Cats will likely support it more naturally.

## Converting from/to other effects

Cats-Effect provides a hierarchy of type classes that open the door to conversion between effects.

Monix BIO provides:
- `monix.bio.TaskLike` to convert other effects to `BIO` with nice `BIO.from` syntax.
- `monix.bio.TaskLift` to convert `BIO` to a different type with `bio.to[F]` syntax.

### cats.effect.IO

Going from `cats.effect.IO` to `BIO` is very simple because `IO` does not need any runtime to execute:

```scala mdoc:silent:reset
import monix.bio.BIO

val io = cats.effect.IO(20)
val bio: BIO[Throwable, Int] = BIO.from(io)
```

Unfortunately, we need `Scheduler` in scope to go the other way:

```scala mdoc:silent:reset
import monix.bio.BIO
import monix.execution.Scheduler.Implicits.global

val bio: BIO[Throwable, Int] = BIO(20)
val ioAgain: cats.effect.IO[Int] = bio.to[cats.effect.IO]
```

### monix.eval.Task

`BIO` does not have `monix.eval.Task` in dependencies, but since they both use `Scheduler` to run, we can do it
without requiring any implicit in scope, if we use `deferAction`:

```scala mdoc:silent:reset
import monix.bio.BIO

val task = monix.eval.Task(20)
val bio: BIO[Throwable, Int] = BIO.deferAction(implicit s => BIO.from(task))
val taskAgain: monix.eval.Task[Int] = monix.eval.Task.deferAction(implicit s => bio.to[monix.eval.Task])
```

In the future, we might introduce a type class in `monix-execution`, which will allow this conversion without any tricks with `deferAction`.

### zio.ZIO

To convert from `ZIO`, you will need `ConcurrentEffect[zio.Task]` instance.
It can be derived with [zio-interop-cats](https://github.com/zio/interop-cats) if you have `zio.Runtime` in scope:

```scala mdoc:silent:reset
import monix.bio.BIO
import zio.interop.catz._

implicit val rts = zio.Runtime.default

val z = zio.ZIO.effect(20)
val bio: BIO[Throwable, Int] = BIO.from(z)
```

The other direction requires `Scheduler` in scope:

```scala mdoc:silent:reset
import monix.bio.BIO
import zio.interop.catz._

implicit val s = monix.execution.Scheduler.global

val bio: BIO[Throwable, Int] = BIO(20)
val zioAgain: zio.Task[Int] = bio.to[zio.Task]
```
Loading

0 comments on commit fd63561

Please sign in to comment.