package ch_01

final case class IO[A](unsafeRun: () => A):

  def map[B](f: A => B): IO[B]         = IO(() => f(unsafeRun()))
  def flatMap[B](f: A => IO[B]): IO[B] = IO(() => f(unsafeRun()).unsafeRun())

// ex. IO which returns the current time
val clock: IO[Long] = IO(() => System.currentTimeMillis())

// ex. IO which measures the duration of a computation
def measure[A](computation: IO[A]): IO[Long] =
  for
    start <- clock
    _     <- computation
    end   <- clock
  yield end - start

// ex. IO which prints to the console
def putStrLn(line: String): IO[Unit] = IO(() => println(line))

// ex. IO which reads from the console
def readStrLn(): IO[String] = IO(() => io.StdIn.readLine())

// IO bridges functional and imperative programming
val program: IO[Unit] =
  for
    _         <- putStrLn("args[0]")
    firstArg  <- readStrLn()
    _         <- putStrLn("args[1]")
    secondArg <- readStrLn()
    _         <- putStrLn(s"$firstArg $secondArg")
  yield ()