Skip to content

Instantly share code, notes, and snippets.

@yannick-cw
Created August 31, 2017 15:00
Show Gist options
  • Save yannick-cw/624c9dcb5d25a43fb8577c1637db1001 to your computer and use it in GitHub Desktop.
Save yannick-cw/624c9dcb5d25a43fb8577c1637db1001 to your computer and use it in GitHub Desktop.
import scala.annotation.tailrec
object IOExample extends App {
import ioInstances._
case class Player(name: String, score: Int)
val winner: (Player, Player) => Option[Player] = (p1, p2) =>
if (p1.score > p2.score) Some(p1)
else if (p2.score > p1.score) Some(p2)
else None
val winnerMsg: Option[Player] => String = p =>
p.map(p => s"${p.name} is the winner!").getOrElse("It's a draw.")
def contest(p1: Player, p2: Player): IO[Unit] =
PrintLine((winner.tupled andThen winnerMsg)(p1, p2))
/**
* using flatMap
*/
for {
_ <- PrintLine("Please state p1 name: ")
name <- ReadLine
_ <- contest(Player(name, 12), Player("B", -33))
} yield ()
/**
* traversing over a list of IOs
*/
val allNames = IO
.traverse(List("Please state p1 name: ", "Please state p2 name: ", "Please state p3 name: "))(
AskAndRead)
allNames.flatMap(seq => PrintLine(seq.mkString(" | ")))
/**
* Looping until
*/
IO.doWhile(AskAndRead("Tell mee your name: "))(name =>
IO(name.length >= 3).flatMap {
case true => PrintLine("Your name is " + name).map(_ => true)
case false => PrintLine("Name is too short").map(_ => false)
})
/**
* Lettings the stack flow over o.O
*/
IO.doWhile(PrintLine("you'll die"))(_ => IO(false))
/**
* Stack with SafeIO
*/
TrampoliningIO.doWhile(SafeIOOps.PrintLine("you'll live forever"))(_ => TrampoliningIO(false))
/**
* SafeIO
*/
val res = for {
_ <- SafeIOOps.PrintLine("Hi")
n <- SafeIOOps.AskAndRead("Give me name")
_ <- SafeIOOps.PrintLine(n)
} yield ()
res.run
}
sealed trait IO[A] {
def run: A
def map[B](f: A => B): IO[B] = IO.map(this, f)
def flatMap[B](f: A => IO[B]): IO[B] = IO.flatMap(this, f)
}
object IO {
def apply[A](effect: => A): IO[A] = pure(effect)
def pure[A](a: => A): IO[A] = new IO[A] { def run: A = a }
def map[A, B](fa: IO[A], f: A => B): IO[B] = flatMap(fa, (a: A) => pure(f(a)))
def flatMap[A, B](fa: IO[A], f: A => IO[B]): IO[B] = new IO[B] {
def run: B = f(fa.run).run
}
def traverse[A, B](li: Seq[A])(f: A => IO[B]): IO[Seq[B]] = sequence(li.map(f))
def sequence[A](li: Seq[IO[A]]): IO[Seq[A]] =
li.foldLeft(IO(Seq.empty[A]))((ios, io) => ios.flatMap(xs => io.map(x => xs :+ x)))
def doWhile[A](a: IO[A])(cond: A => IO[Boolean]): IO[Unit] =
for {
done <- a
isGood <- cond(done)
_ <- if (isGood) pure(()) else doWhile(a)(cond)
} yield ()
}
object ioInstances {
def ReadLine: IO[String] = IO(scala.io.StdIn.readLine())
def PrintLine(msg: String): IO[Unit] = IO(println(msg))
def AskAndRead(ask: String): IO[String] = PrintLine(ask).flatMap(_ => ReadLine)
}
sealed trait TrampoliningIO[A] {
def run: A = TrampoliningIO.run(this)
def map[B](f: A => B): TrampoliningIO[B] = TrampoliningIO.map(this, f)
def flatMap[B](f: A => TrampoliningIO[B]): TrampoliningIO[B] = TrampoliningIO.flatMap(this, f)
}
case class Return[A](a: A) extends TrampoliningIO[A]
case class Suspend[A](resume: () => A) extends TrampoliningIO[A]
case class FlatMap[A, B](fa: TrampoliningIO[A], f: A => TrampoliningIO[B]) extends TrampoliningIO[B]
object TrampoliningIO {
def apply[A](a: => A): TrampoliningIO[A] = Suspend(() => a)
def map[A, B](fa: TrampoliningIO[A], f: A => B): TrampoliningIO[B] =
flatMap(fa, f.andThen(Return(_)))
def flatMap[A, B](fa: TrampoliningIO[A], f: A => TrampoliningIO[B]): TrampoliningIO[B] =
FlatMap(fa, f)
@tailrec def run[A](io: TrampoliningIO[A]): A = io match {
case Return(a) => a
case Suspend(r) => r()
case FlatMap(ioA, f) =>
ioA match {
case Return(a) => run(f(a))
case Suspend(r) => run(f(r()))
case FlatMap(ioY, g) => run(ioY.flatMap(a => g(a).flatMap(f)))
}
}
def doWhile[A](a: TrampoliningIO[A])(cond: A => TrampoliningIO[Boolean]): TrampoliningIO[Unit] =
for {
done <- a
isGood <- cond(done)
_ <- if (isGood) TrampoliningIO(()) else doWhile(a)(cond)
} yield ()
}
object SafeIOOps {
def PrintLine(s: String): TrampoliningIO[Unit] = TrampoliningIO(println(s))
def ReadLine: TrampoliningIO[String] = TrampoliningIO(scala.io.StdIn.readLine())
def AskAndRead(ask: String): TrampoliningIO[String] = PrintLine(ask).flatMap(_ => ReadLine)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment