Skip to content

Instantly share code, notes, and snippets.

@ChristopherDavenport
Last active February 5, 2021 15:44
Show Gist options
  • Save ChristopherDavenport/9b4b64add09b8540447c7a8aeece5c30 to your computer and use it in GitHub Desktop.
Save ChristopherDavenport/9b4b64add09b8540447c7a8aeece5c30 to your computer and use it in GitHub Desktop.
Simple FakeIO without stack-safety.
import cats.effect._
// import cats._
import cats.syntax.all._
import scala.concurrent.duration._
trait FakeIO[A]{
def unsafeRunAsync(f: Either[Throwable, A] => Unit): Unit
}
object FakeIO {
def delay[A](thunk: => A): FakeIO[A] = Async{cb: (Either[Throwable, A]=> Unit) => cb(Either.catchNonFatal(thunk))}
def async[A](f: (Either[Throwable, A] => Unit) => Unit): FakeIO[A] = Async{cb =>
try f(cb)
catch { case scala.util.control.NonFatal(e) => cb(Left(e))}
}
def pure[A](a: A): FakeIO[A] = Async(cb => cb(Right(a)))
def raiseError[A](e: Throwable): FakeIO[A] = Async(cb => cb(Left(e)))
implicit val async = new cats.effect.Async[FakeIO]{
def pure[A](x: A): FakeIO[A] = pure(x)
def raiseError[A](e: Throwable): FakeIO[A] = raiseError(e)
def handleErrorWith[A](fa: FakeIO[A])(f: Throwable => FakeIO[A]): FakeIO[A] = HandleErrorWith(fa, f)
def flatMap[A, B](fa: FakeIO[A])(f: A => FakeIO[B]): FakeIO[B] = FlatMap(fa, f)
def tailRecM[A, B](a: A)(f: A => FakeIO[Either[A,B]]): FakeIO[B] = ??? // Not Included for Simplicity of Impl
def bracketCase[A, B](acquire: FakeIO[A])(use: A => FakeIO[B])(release: (A, ExitCase[Throwable]) => FakeIO[Unit]): FakeIO[B] =
Bracket(acquire, use, release)
def suspend[A](thunk: => FakeIO[A]): FakeIO[A] = flatMap(delay(thunk))(identity)
def async[A](k: (Either[Throwable,A] => Unit) => Unit): FakeIO[A] = async(k)
// Not Included for simplicity
def asyncF[A](k: (Either[Throwable,A] => Unit) => FakeIO[Unit]): FakeIO[A] = AsyncF(k)
}
def unsafeRunSync[A](io: FakeIO[A]): A = {
val latch = new java.util.concurrent.CountDownLatch(1);
var a : Either[Throwable, A] = null
io.unsafeRunAsync{e =>
a = e
latch.countDown()
}
latch.await
a match {
case Right(a) => a
case Left(e) => throw e
}
}
def unsafeToFuture[A](fio: FakeIO[A]): scala.concurrent.Future[A] = {
val p = scala.concurrent.Promise[A]
fio.unsafeRunAsync{e =>
val tryE = e.fold(left => scala.util.Failure(left), right => scala.util.Success(right))
p.complete(tryE)
()
}
p.future
}
def fromTimer(timer: java.util.Timer): Timer[FakeIO] = new Timer[FakeIO]{
def clock: Clock[FakeIO] = new Clock[FakeIO] {
override def realTime(unit: TimeUnit): FakeIO[Long] =
FakeIO.delay(unit.convert(System.currentTimeMillis(), MILLISECONDS))
override def monotonic(unit: TimeUnit): FakeIO[Long] =
FakeIO.delay(unit.convert(System.nanoTime(), NANOSECONDS))
}
def sleep(duration: FiniteDuration): FakeIO[Unit] = FakeIO.async{cb =>
val tick = new java.util.TimerTask {
def run() = cb(Right(()))
}
timer.schedule(tick, duration.toMillis)
}
}
implicit val globalTimer: Timer[FakeIO] = fromTimer(new java.util.Timer(true))
private final case class Async[A](f: (Either[Throwable, A] => Unit) => Unit) extends FakeIO[A] {
def unsafeRunAsync(f2: Either[Throwable,A] => Unit): Unit = f(f2)
}
// Not sure about this one
private final case class AsyncF[A](f: (Either[Throwable, A] => Unit) => FakeIO[Unit]) extends FakeIO[A] {
def unsafeRunAsync(f2: Either[Throwable,A] => Unit): Unit = f(f2).unsafeRunAsync{
case Left(_) => ()
case Right(_) => ()
}
}
private final case class FlatMap[A, B](fa: FakeIO[A], f: A => FakeIO[B]) extends FakeIO[B] {
def unsafeRunAsync(f2: Either[Throwable, B] => Unit): Unit = fa.unsafeRunAsync{
case Right(a) =>
try f(a).unsafeRunAsync(f2)
catch {case scala.util.control.NonFatal(e) => f2(Left(e))}
case Left(e) => f2(Left(e))
}
}
private final case class HandleErrorWith[A](fa: FakeIO[A], f: Throwable => FakeIO[A]) extends FakeIO[A] {
def unsafeRunAsync(f2: Either[Throwable,A] => Unit): Unit = fa.unsafeRunAsync{
case Left(e) => f(e).unsafeRunAsync(f2)
case r@Right(_) => f2(r)
}
}
private final case class Bracket[A,B](acquire: FakeIO[A], use: A => FakeIO[B], release: (A, ExitCase[Throwable]) => FakeIO[Unit]) extends FakeIO[B] {
def unsafeRunAsync(f: Either[Throwable,B] => Unit): Unit = {
acquire.unsafeRunAsync{
case Left(e) => f(Left(e))
case Right(a) => use(a).unsafeRunAsync{
case Left(e) =>
release(a, ExitCase.error(e)).unsafeRunAsync{
case Left(e) => f(Left(e))
case Right(_) => f(Left(e))
}
case Right(b) =>
release(a, ExitCase.complete[Throwable]).unsafeRunAsync{
case Left(e) => f(Left(e))
case Right(_) => f(Right(b))
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment