IO

class IO[A](sideEffect: => A) {
  lazy val run: A = sideEffect
  def map[B](f: A => B): IO[B] =
    new IO(f(run))
  def flatMap[B](f: A => IO[B]): IO[B] =
    new IO(f(run).run)
}

Example

val program: IO[Unit] =
  for {
    _   <- new IO(println("Type something:"))
    in  <- new IO(readLine())
    out  = "You typed: " + in
    _   <- new IO(println(out))
  } yield ()

println("Running program...")
program.run