1. Overview

Most of the modern large-scale applications are reactive in nature. Such systems follow an event-driven programming paradigm and are generally highly scalable and resilient to failures. There are many tools available for us to build such systems. Monix is one of such offerings for Scala and Scala.js.

In this short tutorial, we’ll familiarize ourselves with this library.

2. What Is Monix?

Monix is a high-performance Scala library built for composing asynchronous and event-driven programming. The name Monix originates from Monads and Rx. It began as an implementation of ReactiveX. Hence, it supports backpressure handling and ReactiveStreams protocols by default. It offers concurrency primitives such as Observable, Task, and many more. Being a Typelevel project, it’s compatible with other Typelevel libraries such as Cats.

Let’s look at an example using the Task data type to construct an asynchronous program:

def sampleMonixTask(a:Int, b:Int): Task[Int] = Task {
  val result = {
    a + b
  }
  result
}
val task = sampleMonixTask(5, 5)

The Task data type is lazy by default. Therefore, to start the execution of the program, we need a trigger. Let’s call the runToFuture method and examine the result:

val f = task.runToFuture
println(f.value)  // Prints Some(Success(10))

There are several other methods to run the task. Let’s look at the runAsync method that takes a callback method and returns a Cancelable:

val cancelable = task.runAsync { result =>
  result match {
    case Right(value) => println(value)
    case Left(ex) => println(s"Exception: ${ex.getMessage}")
  }
} // prints 10

Moreover, there are several advantages of using the Task data type. For one, it gives us more fine-grained control over the execution — we can decide how and when the asynchronous program executes.

3. Monix Sub-Projects

The Monix project has followed a modular design. As a result, it’s composed of several sub-projects. This helps us to pick and choose only those components required for our projects. Let’s go through each one of them and see what they’re used for.

3.1. monix-execution

Let’s start with the monix-execution module. This module provides us the low-level primitive types for asynchronous execution. It’s built on top of the scala.concurrent package*.* Scheduler, Cancelable, and Callback are some of the useful members of this module.

3.2. monix-catnap

Next is the monix-catnap module. It provides us generic and purely functional abstractions for managing concurrency. It’s built on top of Cats-Effects. CircuitBreaker and MVar are the major components in this module.

3.3. monix-eval

The monix-eval module helps in dealing with purely functional effects in a principled manner. Task and Coeval are major components for controlling side-effects and effectively dealing with concurrency.

3.4. monix-reactive

The monix-reactive module implements the Observer pattern using the ReactiveX design guidelines. It exposes the Observable data type, a high-performance streaming abstraction.

3.5. monix-tail

Finally, the monix-tail module provides us utilities for asynchronous streaming using a pull-based protocol. It exposes the highly composable and flexible streaming data type Iterant.

4. Conclusion

In this tutorial, we’ve seen the Monix library and its applications. Then we saw an example using the Task data type and some of its advantages over Scala’s standard library. After that, we saw the list of sub-projects within Monix and learned the components and functionalities of each of them.

As always, the full source code of the article is available over on GitHub.