In this short blog post I will try, in 10 minutes or less, to present what Monix library is and convince you that it is good to know it.
Formerly known as Monifu, Monix is a library for asynchronous programming in Scala and Scala.js
It contains several abstractions that are useful and sometimes can be found superior to vanilla Scala or Akka counterparts. But for sure this post is not about deprecating use of Akka actors or streams. It is about another tool in Scala programmers box. I am going to present some of the abstractions that Monix gives and conclude why they are valuable.
Monix comes with
Task abstraction that can be seen as a replacement for
However, there are some major differences.
Future was designed to be ongoing computation
that is possibly finished,
Task is a description of computation.
So when using
Task as a drop in replacement for
Future, one has to trigger its execution to start computation.
This means that following code will not print anything:
To start execution one has to do two things:
foreachto execute computation
Scheduler is an
ExecutionContext that additionally allows scheduling single or
periodical execution of
CancelableFuture allow programmer to attempt cancellation
of some computation before is ends, when it is desired to stop it.
That is the second major difference I see and it is a nice improvement in my opinion.
Monix allows high level of control over
execution. One can choose if task body should be executed on
each invocation of
or if result should be memoized (
or if execution should always execute in separate thread (
On top of that
Task has an API to choose scheduler it should run on.
Let me show one, contrived example, that shows some errors handling and schedulers choice.
Code below gets
Picture and should perform analysis that tells if given picture
was painted by Picasso or not. First, it runs analysis locally, but in case when the local algorithm
cannot decide, remote analysis service is used. Finally, original picture and analysis results
are stored in a database. Of course, services are just mocks, thus results are hardcoded for sake of
I have mentioned that
Schedulers are like
ExecutionContext with an ability to
schedule execution on them. But there is one more important trait
ExecutionModel which describes if tasks should be executed
in separate logical threads or if trampoline should be used or if batch of
tasks should be executed synchronously and then a new thread should be used.
Just a few more words about
Task. It comes with plenty of combinators and constructors,
counterparts for both
Future and Scalaz
Task combinators. I will just few that grabbed my attention:
onErrorRestart- restart until completes successfully
restartUntil- restart until result fulfills predicate
timeout- adds timeout to existing
Task, Monix gives you also
FutureUtils.timeoutto timeout Scala
To recap: Monix
Task gives users a great dose of control over asynchronous computation execution
by a rich set of builders and combinators, ability to tune schedulers and execution models
and also by out-of-the-box possibility to timeout or cancel computations. From my point of
view this is very considerable profit.
There are two artifacts available:
monix-scalaz72 with code that turns
into an instance of
Monad from Cats or Scalaz.
Coeval is synchronous counterpart for
it gives control over evaluation, side effects and error handling, all at once,
but for synchronous computations.
Task it does not run when defined but only when
run is called.
One can define
Coeval to execute on each
value call with
to memoize result (like
This level of control allows one to remove stack overflows caused by tail recursion.
@tailrec can do that also but only for simple tail recursion that involves one function.
Coeval has many builders, combinators (
restartUntil, error handing and others) and transformers (from/to
Again a bit made up example, this time user will be prompted to enter a number. User enters invalid line 3 times, other prompt will be used and in case of error program falls back to 42.
Cats and Scalaz integrations contain
Monad instances of
Coeval (and other tools mentioned here).
Before I write a really short part about
MVar: I have never written “real” code that used this or analogical abstraction.
take something from
MVar but both operations asynchronously block,
what is expressed in return types of
takes something from empty
MVar it gets
Task[A] that will complete when
puts value to it.
put behaves similar, returns
Task that completes only
MVar put was executed (what requires access to empty
Obligatory example, this time dummy producer-consumer problem.
is more verbose version with some
printlns to show lazy behaviour
Task as well.
MVar is inspired by Haskell’s MVar but original blocks thread instead of returning asynchronous
computation abstraction (
Task). That is because threads in Haskell are not so scarce.
Last part of the library I would like to mention in this article is Monix’s take on reactive streams.
I will describe it shortly and only from perspective of user,
not implementer of new
Observable is equivalent of
org.reactivestreams.Publisher or akka-streams
Observer is like
Consumer, specifies how to consume an
Observable has wealth of combinators: for grouping, filtering, mapping, taking, sliding,
giving control when upstream or downstream are slow, attaching callbacks for upstream events
like stream end, error handling and more. I am not going to enumerate these but I can assure
that library author gave extraordinary effort in that matter.
Provided set of
Consumers is sufficient, I was able to find replacement for each
Sink that I have used in real project based on akka-streams.
I will show what unfolded when I took an akka-stream used in a recent project I worked on and
rewritten it with
Monix. I have changed business methods to dummy ones.
First, natural language description of processing, then
implementation and the last will be Monix implementation.
Full example is available in GH repo.
API looks similar to some extent. The main difference is that Monix streams are run on
Materializer, which most probably is
ActorMaterializer, which in turn
Scheduler is much more lightweight than
I have also written and ran some JMH benchmarks for totally made up set of operations although I will not summarize them as a comparison to avoid possibly false conclusions. Surely Monix streams are very efficient.
Coeval there is glue library that provides Cats or Scalaz Monads instances for
Because it gives you a very good set of tools for asynchronous programming.
Task as an alternative to
Future, with many combinators and helpers to
write your asynchronous code.
It provides well engineered and efficient implementation of reactive streams.
MVar type can sometimes be the simplest solution for synchronization problems.
There are also several miscellaneous classes I have not written about, that can be handy,
like Atomic that can
AtomicInt and alike, or CircutBreaker,
or Future Utils.