Skip to content

Instantly share code, notes, and snippets.

@jdegoes
Last active November 11, 2017 17:02
Show Gist options
  • Save jdegoes/c0572586e4a18e761796cc3707e7978f to your computer and use it in GitHub Desktop.
Save jdegoes/c0572586e4a18e761796cc3707e7978f to your computer and use it in GitHub Desktop.
Pedagogical synchronous and asynchronous effect monads in Scala.
case class IO[A](unsafePerformIO: () => A) { self =>
def map[B](ab: A => B): IO[B] =
IO(() => ab(unsafePerformIO()))
def flatMap[B](afb: A => IO[B]): IO[B] =
IO(() => afb(unsafePerformIO()).unsafePerformIO())
def attempt: IO[Either[Throwable, A]] =
IO(() => try { Right(unsafePerformIO()) } catch { case t : Throwable => Left(t) })
def forkIO: Task[A] = Task(f => IO(unsafePerformIO = () => {
new java.lang.Thread {
override def run: Unit = {
f(self.attempt.unsafePerformIO()).unsafePerformIO()
}
}
}))
}
object IO {
def point[A](a: => A): IO[A] = IO(() => a)
def fail[A](t: => Throwable): IO[A] = IO(unsafePerformIO = () => throw t)
}
case class Task[A](run: (Either[Throwable, A] => IO[Unit]) => IO[Unit]) {
def attempt: Task[Either[Throwable, A]] = Task[Either[Throwable, A]](f => run(e => f(Right(e))))
def map[B](f: A => B): Task[B] = Task(g => run {
case Left(t) => g(Left(t))
case Right(a) => g(Right(f(a)))
})
def flatMap[B](f: A => Task[B]): Task[B] = Task(g => run {
case Left(t) => g(Left(t))
case Right(a) => f(a).run(g)
})
}
object Task {
def lift[A](io: IO[A]): Task[A] = Task(f => io.attempt.flatMap(f))
def point[A](a: => A): Task[A] = Task(f => f(Right(a)))
def fail[A](t: => Throwable): Task[A] = Task(f => IO(unsafePerformIO = () => f(Left(t))))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment