liveBook · Manning

unsafeRun

As explained in Functional Programming in Scala, Second Edition

unsafeRun is a book-defined operation that executes an IO computation and returns its result while permitting its external effects.

unsafeRun interprets an IO value: it performs the described external effects and returns the computation’s result. Building an IO remains pure until this boundary is crossed, keeping composition separate from console or other environmental manipulation. section 13.2 section 13.2.1

Its role changes as the IO representation becomes more expressive:

  • A basic IO exposes unsafeRun: Unit, so it can execute actions such as PrintLine but cannot return meaningful input. section 13.2
  • A parameterized IO[A] returns an A, allowing ReadLine, parsing, sequencing, mapping, and flatMapping while preserving delayed effects. section 13.2.1
  • A direct recursive interpreter can overflow the stack when flatMap nests indefinitely, as with a forever-running print action. section 13.3
  • A constructor-based IO represents Return, Suspend, and FlatMap explicitly, letting unsafeRun interpret the structure with a tail-recursive loop. section 13.3.1

Executing a composed computation

A converter can return an IO[Unit] that prints a prompt, reads a line, converts it, and prints the result. Calling converter.unsafeRun performs those effects; constructing converter does not. section 13.2.1

@annotation.tailrec final def unsafeRun(): A = this match
  case Return(a) => a
  case Suspend(r) => r()
  case FlatMap(x, f) => x match                       #1
    case Return(a) => f(a).unsafeRun()
    case Suspend(r) => f(r()).unsafeRun()             #2
    case FlatMap(y, g) =>
      y.flatMap(a => g(a).flatMap(f)).unsafeRun()     #3

Inspect the tail-recursive unsafeRun interpreter and its handling of Return, Suspend, and nested FlatMap cases. section 13.3.1

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Defines typed results, shows direct recursion, and develops constructor-based tail recursion to avoid stack overflow.
Examples
Uses PrintLine, ReadLine, a Fahrenheit converter, an interactive factorial REPL, and an infinite printing computation.
What it assumes
Assumes basic Scala functions and effects, then develops typed IO, monadic composition, and interpreter control flow.
What you can do after
Evaluate composed IO programs while recognizing why direct recursive interpretation can overflow and how explicit constructors prevent it.
How it connects
Connects IO with referential transparency, monads, map, flatMap, delayed evaluation, and tail recursion.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Programming in Scala, Second Edition. The section links point to the parts of the book it draws on.