Functional programming (FP) is easy if you just have to write pure functions that do some calculation based on some inputs and return some output. However, most useful programs involve side effects, for instance querying a database system, calling an internal or external API, reading/writing files or even printing to console. The act of performing any of those side effects is considered an “effectful operation”. In FP, we can use an effect system for dealing with any effectful operations.
The Scala standard library does not have a functional effect system. Fortunately there are a few really popular and mature open source libraries in the Scala ecosystem. For example, Cats Effect, Monix (Task) and ZIO provide an effect runtime and some useful constructs to work with functional effect.
I will use Cats Effect as an example as I am most familiar with it.
The Cats Effect library has a data type,
IO[A] which you can think of as a blueprint that,
when evaluated, can perform effectful operations and finally return a value of type
val getUsersIO = IO(db.query(...))
The code above creates an
IO with an effectful operation
db.query(...) has to query a database system).
Note that by creating an
IO, any effectful operation is not executed yet.
IO[A] is a blueprint that describes some effectful operations and
will only perform those effectful operations when evaluated.
How do we evaluate a Cats Effect
IO? We can do that by calling
unsafeRunSync() on the
This will evaluate the IO value, perform the side effects as described and return the result of type
which could be a
List[User] in this case.
Having said that, we should only run
unsafeRunSync() or similar methods at the very edge of our program
which is the entry point of our programs, ie. the main method. As the method name
the method will perform the side effect right away and therefore should be avoided throughout the rest of
You have most likely used
Future to perform asynchronous computation.
Future is available in the Scala standard library and works for the side effects described above so
what is wrong with
Future? Why do we still need functional effects?
Future is evaluated eagerly whereas a functional effect like Cats Effect
IO is evaluated lazily.
What that means is that a
Future performs side effects immediately upon creation whereas a Cats Effect
is a blueprint for which the side effects will only be performed when we call something like
unsafeRunSync() on it. This is best illustrated by the comic below which is my favorite.
(Credits to impurepics.com)
This means that we can’t compose
Future as values and using
Future won’t make your program
referentially transparent (this is a topic that deserves a blog post on its own).