Typelevel alchemist

Cats ecosystem in practice

Jakub Kozłowski - Scala Developer, Ocado Technology Scala Wave | September 7, 2018 | Gdańsk, Poland


  1. Typeclasses recap
  2. Higher-kinded types
  3. Referential transparency
  4. Tagless Final
  5. Cats intro
  6. Cats-effect
  7. fs2, http4s, doobie
  8. patterns for applications

About me

  • Live in Wrocław, Poland
  • Doing Scala for 3 years
  • Worked @ Scalac all that time
  • now @ Ocado Technology
  • I love ramen
  • Running and lifting when not injured


Typeclasses recap

  • What's a typeclass?
  • Examples
  • Coherence
  • Alternatives

What's a typeclass

Example: collapsing a list

Collapsing a list

Hard to extend - need a new implementation AND name for every type in list
Let's try to make that abstract

Original idea from the Cats documentation

Monoid typeclass

Usage of new implementation

Typeclasses should have laws


  • Each pairing of (type, typeclass implementation) must be unique
  • Not enforced by Scala

Takeaway: Not all traits with implicit instances are typeclasses


Runtime reflection

  • e.g. JSON serializers in Jackson or json4s
  • Hard to extend (registering instances in global, mutable scope)
  • No compile-time checks, no runtime safety

Pattern matching

  • Non-extensible
  • No compile-time checks, no runtime safety
  • Still hard with generics (requires ClassTag, which is... a typeclass)


Extensible Generics Runtime safe
Pattern matching

Polymorphism via subtyping


  • How to collapse an empty list?
  • Pollutes the implementing classes
  • All or nothing
  • Can't implement for types we can't control

So, typeclasses...

Standard library examples (pre-2.13)

Higher-kinded types



String, Int, Boolean, List[Int]

higher-kinded (* -> *)

Option[_], List[_], Future[_]

higher-kinded types are "incomplete" - they have a hole

also called "type constructors"

Higher-kinded types

Type-level functions

f      =  x  => f(x)      //function lambda
Option = [A] => Option[A] //type lambda

Higher-kinded types need to be applied with a type to become a *-kinded type

Functions vs higher-kinded types

Abstracting on types

Type lambdas

With kind-projector

Why we need type lambdas

Partial unification

The compiler can infer this (with a flag, by default in 2.13):
Unfortunately, IDEA can't (yet) :(

Tagless Final

Fancy name for abstracting on types*
* - not a precise definition, but it'll suffice

Why tagless?

  • We don't couple ourselves to a specific effect like IO
  • We can easily swap effects for testing or if we decide to switch
  • Easier to work with monad transformers (explained later)
  • Sometimes allows for not leaking implementation details
  • More power to users of your library

Referential transparency

If an expression t can be substituted by its value a, then it's referentially transparent
//if a function f(x) is referentially transparent
//and x is referentially transparent

val a = f(x)

(a, a) <-> (f(x), f(x))

Breaking RT - Future

Future breaks most of the time

Future can break even more

It hurts

It also breaks in the other direction

Throwing breaks too

What's a (pure) function?

Meet IO

Measure time with IO

Predictable nukes with IO

Why go referentially transparent?

  • Equational reasoning
  • Programs as values
  • Testing
  • Local reasoning
  • Explicit effects, predictable code

Equational reasoning

Refactor fearlessly

Programs as values

Pass your programs to others so that they add meaning


Local reasoning

  • Split programs more easily
  • Compose smaller functions into larger ones
  • Reason independently

Local reasoning

A wild implicit side effect appears!

Find the side effect

(line 13, limit)

Explicit effects


  • Is it calling the DB?
  • Is it calling HTTP?
  • Is it logging?
  • Is it caching?
  • Is it changing state?
  • Is it reading from a cache?
  • Can it throw?
  • Can it have a different result if you call it tomorrow?

If you have ≥1 checks, it's effectful - slap an IO on it

Predictable results

A pure function can have a limited number of implementations
def f[A](a: A): A

has one

How many implementations?

def f9: IO[Unit]

How many implementations?

def f9: IO[Unit]

How many implementations?

How many implementations?



Core typelevel project

  • Typeclasses
  • Data types
  • Syntax

Typeclasses in cats

  • Eq
  • Order
  • Monoid
  • Functor
  • Applicative
  • Monad
  • ApplicativeError
  • MonadError
  • Traverse...


Multiversal equality

Note: typeclass definitions in the slides are simplified

Syntax for Cats typeclasses

Tip: use consistent import style to avoid conflicts

Eq exercise

Write a function that checks if all elements of a List[T] are equal


Comparison, ordering

Order exercise

Write a function that finds the largest element of a List[T]


Text representation - type-safe toString

Show exercise

Write an instance of Show[List[T]]

Semigroup, Monoid

binary operation + (with monoid) identity element


map, abstracted

Functor laws

There are two rules of the functor club.

//identity law
fa.map(id) == fa
//composition law
fa.map(f).map(g) == fa.map(f.andThen(g))

Functor exercise

Write a function that adds 42 to all elements in a functor


(potentially parallel) combination of independent effects, wrapping value in effect

The more real Applicative


Sequential composition of effects

Monad laws

//left identity
pure(a).flatMap(f) == f(a)
//right identity
fa.flatMap(pure) == fa
fa.flatMap(f).flatMap(g) == fa.flatMap(a => f(a).flatMap(g))


raising errors, recovering from them


Running predicates, transforming errors, un-recovering


sequence flips effects

Traverse examples

Traverse vs sequence

fa.traverse(f) == fa.map(f).sequence
fa.sequence == fa.traverse(identity)

Data types

  • NonEmptyList
  • Validated
  • FunctionK
  • OptionT
  • EitherT

NonEmptyList (NEL)

A list that's guaranteed not to be empty

With safe `head`, `tail` operations


like Either, but with error accumulation


type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A]
Useful for error accumulation, because NEL has a semigroup
type EitherNel[+E, +A] = Either[NonEmptyList[E], A]

Either vs Validated: Applicative

In English

  • Either: if the first one fails, we fail with its error. Otherwise se check the second one.
  • Validated: if both are failed, errors are combined using the semigroup instance.

Applicative builder syntax

No need to call `product`, `map2` or `ap` directly


One more typeclass - Parallel

Not just parallelism

Parallel explained

  • A bi-directional transformation between two isomorphic types with applicatives
  • One has a monad, the other one can't

An applicative that can't be a monad does e.g. parallel execution of IO, or error accumulation like Validated

Parallel applicative syntax

Later - parallel IO





EitherT usage


An IO monad for cats (and more)


  • More data types (IO, concurrency primitives)
  • More instances


  • Just a data structure
  • A pure, immutable value
  • Preserves referential transparency
  • When evaluated, can perform effects before returning a value of type A

Supports synchronous AND asynchronous effects

IO[A] is not a stream

An IO, when evaluated, will do one of these:

  • Eventually complete with a value of type A
  • fail with a Throwable
  • never complete (e.g. IO.never)

Creating IOs

IO - example

IO - more examples

IO - echo name

The "hello world" of IOs

IO - another example

IO in parallel

IO gotchas

How to run?

at end of world, e.g. IOApp

How to run?


IO without async capabilities

Cats-effect typeclasses

  • Bracket
  • Sync
  • LiftIO
  • Async
  • Effect
  • Concurrent
  • ConcurrentEffect


Ensuring resource safety - loan pattern in Scala

Bracket - example

Bracket - more

Resource data type

Sync typeclass

Suspend side effects in F[_]

Sync - example


Convert an IO to F[_]


Create asynchronous effects

Async - example


Run your effect


Start an effect with the ability to cancel

Concurrent example


Run a (potentially asynchronus) effect synchronously with the ability to cancel it

Cats-effect datatypes

  • IO
  • SyncIO
  • Fiber
  • Ref & friends
  • Clock
  • ContextShift
  • Timer


Thread-safe, purely functional mutable state

Ref - example


Provides time


Shifts execution to a thread pool

Usually created with an EC (for IO)


Schedules delayed tasks (sleep), has a clock


Stream[F[_], A]

A stream of effects in F[_] producing 0..infinity values of type A

fs2 - example

Building streams

Actually running a stream

brick-store project

Thank you!

Get in touch

@kubukoz | kubukoz@gmail.com