Sync

A Monad that can suspend the execution of side effects in the F[_] context.

import cats.MonadError

trait Sync[F[_]] extends MonadError[F, Throwable] {
  def suspend[A](thunk: => F[A]): F[A]
  def delay[A](thunk: => A): F[A] = suspend(pure(thunk))
}

This is the most basic interface that represents the suspension of synchronous side effects. On the other hand, its implementation of flatMap is stack safe, meaning that you can describe tailRecM in terms of it as demonstrated in the laws module.

import cats.effect.{IO, Sync}
// import cats.effect.{IO, Sync}

import cats.laws._
// import cats.laws._

val F = Sync[IO]
// F: cats.effect.Sync[cats.effect.IO] = cats.effect.IOInstances$$anon$1@29a9276

lazy val stackSafetyOnRepeatedRightBinds = {
  val result = (0 until 10000).foldRight(F.delay(())) { (_, acc) =>
    F.delay(()).flatMap(_ => acc)
  }

  result <-> F.pure(())
}
// stackSafetyOnRepeatedRightBinds: cats.laws.package.IsEq[cats.effect.IO[Unit]] = <lazy>

Example of use:

val ioa = Sync[IO].delay(println("Hello world!"))
// ioa: cats.effect.IO[Unit] = IO$1408185156

ioa.unsafeRunSync()
// Hello world!

So basically using Sync[IO].delay is equivalent to using IO.apply.

The use of suspend is useful for trampolining (i.e. when the side effect is conceptually the allocation of a stack frame) and it’s used by delay to represent an internal stack of calls. Any exceptions thrown by the side effect will be caught and sequenced into the F[_] context.