Follow 5 Simple Rules with Cats
Program written using Cats Effect provides incredibly strong guarantees and powerful functionality, performance, safety, and composability, provided you follow each of the following rules:
- Wrap all side-effects in
delay
,async
,blocking
, orinterruptible
- (pro tip: try to keep the size of your
delay
blocks small; twodelay
s with aflatMap
is much better than one bigdelay
)
- (pro tip: try to keep the size of your
- Use
bracket
orResource
for anything which must beclose
d - Never hard-block a thread outside of
blocking
orinterruptible
- Use
IOApp
instead of writing your owndef main
- Never call anything that has the word
unsafe
in the name
import cats.effect._ object Main extends IOApp.Simple { val run = IO.println("Hello, World!") }
Or, if you need the ability to take arguments and return exit codes:
import cats.effect._ object Main extends IOApp { def run(args: List[String]): IO[ExitCode] = if (args.headOption.map(_ == "--do-it").getOrElse(false)) IO.println("I did it!").as(ExitCode.Success) else IO.println("Didn't do it").as(ExitCode(-1)) }
If you follow these rules, and you use libraries and frameworks which also follow these rules, you will get a truly astonishing array of things essentially for free:
- Extremely high performance, elastic, and scalable applications
- Proven backpressure mechanisms under extreme load in real deployments
- Reliable resource safety in all cases
- Aggressive interruption of unnecessary work (e.g. timeouts), automatically, without any extra implementation effort
- Cats Effect provides Composable and modular application architecture (real, practical functional programming)
- Simple, safe, and incredibly powerful concurrency mechanisms that get faster under high contention
- The Highly tuned application runtime with optimized threading and memory management semantics
- Powerful and orthogonal abstractions which enable architectural decomposition that scales to any problem space
- Access to an entire ecosystem of uniquely powerful libraries and tooling
Cats Effect System
The typical Cats Effect system is often built in terms of simple, orthogonal, primitive capabilities which come together to represent all the expressive power necessary to encode a modern asynchronous runtime. Much like how the rules of addition, multiplication, and integers come together to define much of what we understand about basic arithmetic, so too do the rules of Functor
, Monad
, and Concurrent
come together to define the nature of a program which has all the capabilities you need.
Cats Effect isn’t just designed to enable high performance applications with out-of-the-box safety and elasticity under load. It was intended first and foremost as a tool for implementing composable and reasonable software that is easy to write, easy to test, and easy to evolve as your team and requirements change over time. To achieve this goal, Cats Effect embraces and enables strong, typeful, purely-functional programming styles that are uniquely tailored for the Scala language.
Getting started
Add the following to your build.sbt:
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.2.2"
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
To create a new Cats Effect application, place the following contents into a new Scala file within your project:
import cats.effect.{IO, IOApp}
object HelloWorld extends IOApp.Simple {
val run = IO.println("Hello, World!")
}
Once you have saved this file, you should be able to run your application using sbt run
, and as expected, it will print Hello, World!
to standard out.
Testing
The easiest way to write unit tests which use Cats Effect is with MUnit and MUnit Cats Effect. To get started, add the following to your build.sbt:
libraryDependencies += "org.typelevel" %% "munit-cats-effect-3" % "1.0.3" % Test
Acknowledgements
My interest in Cats Effects, as well as some of API in cats-effects was influenced by:
- Cats-effect by typelevel
- Cats Effect The pure asynchronous runtime for Scala by @Typelevel