Suspend is an IO computation constructor that stores a deferred effect rather than executing it immediately. The stored thunk runs only when an interpreter reaches Suspend, separating program construction from effect execution. section 13.3.1
Together with Return and FlatMap, Suspend reifies control flow as data. FlatMap records how to continue after the suspended result, allowing a tail-recursive interpreter to execute the computation without recursively calling unsafeRun. section 13.3.1
The constructor takes different forms as the effect representation becomes more expressive:
- IO uses a thunk, such as Suspend(() => ioa), so by-name arguments remain unevaluated until interpretation. section 13.3.1
- Async uses Suspend with Par, allowing the interpreter to support asynchronous computations rather than waiting on Function0. section 13.4
- Free abstracts the suspended effect as F[A], with TailRec and Async represented as Free[Function0, A] and Free[Par, A]. section 13.4
Suspending output
def printLine(s: String): IO[Unit] = IO(println(s)) constructs an IO value without printing immediately. Repeated calls form a FlatMap structure, and unsafeRun eventually reaches each Suspend and forces its thunk. section 13.3.1
Listing 13.4 Creating a new IO type
enum IO[A]:
case Return(a: A) #1
case Suspend(resume: () => A) #2
case FlatMap[A, B](
sub: IO[A], k: A => IO[B]) extends IO[B] #3
def flatMap[B](f: A => IO[B]): IO[B] =
FlatMap(this, f)
def map[B](f: A => B): IO[B] =
flatMap(a => Return(f(a)))
Inspect the three-constructor IO definition and the flatMap implementation that records continuation structure around Suspend. section 13.3.1
