Monix BIO

Monix BIO

  • API Docs
  • Documentation
  • GitHub

›Documentation

Documentation

  • Introduction
  • Getting Started
  • Creating IO
  • Executing IO
  • Error Handling
  • Resource Safety
  • Cats-Effect Integration
  • Asynchronous Stack Traces
  • Other Effects

Cats-Effect Integration

Monix-BIO provides Cats-Effect integration out of the box. In practice, it means that integration with Typelevel libraries, such as http4s, or doobie should work without much hassle.

Getting instances in scope

All Cats instances up until Effect and ConcurrentEffect (excluded) are available automatically, without any imports.

import cats.syntax.parallel._
import monix.bio.IO

val taskA = IO(20)
val taskB = IO(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 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 IO[Throwable, A].

For instance, let's say we want to use monix.catnap.ConcurrentQueue which exposes an interface built on Cats-Effect type classes so it can be used with any effect:

import monix.bio.{IO, Task}
import monix.catnap.ConcurrentQueue

val queueExample: IO[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 IO, but Concurrent extends Sync, so we need to settle on Throwable error type. Since our F is IO[Throwable, *], all operations on ConcurrentQueue will return IO[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?

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.IOLike to convert other effects to IO with nice IO.from syntax.
  • monix.bio.IOLift to convert IO to a different type with io.to[F] syntax.

cats.effect.IO

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

import monix.bio.IO

val catsIO = cats.effect.IO(20)
val monixIO: IO[Throwable, Int] = IO.from(catsIO)

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

import monix.bio.IO
import monix.execution.Scheduler.Implicits.global

val monixIO: IO[Throwable, Int] = IO(20)
val catsAgain: cats.effect.IO[Int] = monixIO.to[cats.effect.IO]

monix.eval.Task

monix.bio.IO does not include 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:

import monix.bio.IO
import monix.eval.Task

val task = Task(20)
val bio: IO[Throwable, Int] = IO.deferAction(implicit s => IO.from(task))
val taskAgain: Task[Int] = Task.deferAction(implicit s => bio.to[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 if you have zio.Runtime in scope:

import monix.bio.IO
import zio.interop.catz._

implicit val rts = zio.Runtime.default

val z = zio.ZIO.effect(20)
val monixIO: IO[Throwable, Int] = IO.from(z)

The other direction requires Scheduler in scope:

import monix.bio.IO
import zio.interop.catz._

implicit val s = monix.execution.Scheduler.global

val monixIO: IO[Throwable, Int] = IO(20)
val zioAgain: zio.Task[Int] = monixIO.to[zio.Task]
← Resource SafetyAsynchronous Stack Traces →
  • Getting instances in scope
    • Sync and above
  • Converting from/to other effects
    • cats.effect.IO
    • monix.eval.Task
    • zio.ZIO

Copyright © 2019-2021 The Monix Project Developers.