Last active
February 5, 2021 15:44
-
-
Save ChristopherDavenport/9b4b64add09b8540447c7a8aeece5c30 to your computer and use it in GitHub Desktop.
Simple FakeIO without stack-safety.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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