ContextShift is the pure equivalent to:

It provides the means to do cooperative yielding, or on top of the JVM to switch thread-pools for execution of blocking operations or other actions that are problematic.

The interface looks like this:

import scala.concurrent.ExecutionContext

trait ContextShift[F[_]] {

  def shift: F[Unit]

  def evalOn[A](ec: ExecutionContext)(f: F[A]): F[A]

N.B. this is NOT a type class, meaning that there is no coherence restriction. This is because the ability to customize the thread-pool used for shift is essential on top of the JVM at least.


The shift operation is an effect that triggers a logical fork.

For example, say we wanted to ensure that the current thread isn’t occupied forever on long running operations, we could do something like this:

import cats.effect._
import cats.implicits._

def fib[F[_]](n: Int, a: Long = 0, b: Long = 1)
  (implicit F: Sync[F], cs: ContextShift[F]): F[Long] = {

  F.suspend {
    val next = 
      if (n > 0) fib(n - 1, b, a + b)
      else F.pure(a)
    // Triggering a logical fork every 100 iterations
    if (n % 100 == 0)
      cs.shift *> next


The evalOn operation is about executing a side effectful operation on a specific ExecutionContext, but then “return” to the “default” thread-pool or run-loop for the bind continuation.

import java.util.concurrent.Executors
import scala.concurrent.ExecutionContext
import cats.effect._

def blockingThreadPool[F[_]](implicit F: Sync[F]): Resource[F, ExecutionContext] =
  Resource(F.delay {
    val executor = Executors.newCachedThreadPool()
    val ec = ExecutionContext.fromExecutor(executor)
    (ec, F.delay(executor.shutdown()))
def readName[F[_]](implicit F: Sync[F]): F[String] = 
  F.delay {
    println("Enter your name: ")

object MyApp extends IOApp {

  def run(args: List[String]) = {
    val name = blockingThreadPool[IO].use { ec =>
      // Blocking operation, executed on special thread-pool
    for {
      n <- name
      _ <- IO(println(s"Hello, $n!"))
    } yield ExitCode.Success