Last active
June 25, 2016 04:46
-
-
Save x7c1/e854f69d716265cd975d3261a12d1c56 to your computer and use it in GitHub Desktop.
Substitutes for Reader[ExecutionContext, Future[Either[L, R]]]
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
| trait Fate[X, +L, +R]{ | |
| def map[R2](f: R => R2): Fate[X, L, R2] | |
| def flatMap[L2 >: L, R2](f: R => Fate[X, L2, R2]): Fate[X, L2, R2] | |
| def run[L2 >: L, R2 >: R]: X => (Either[L2, R2] => Unit) => Unit | |
| } | |
| object Fate { | |
| def apply[X, L, R](underlying: X => (Either[L, R] => Unit) => Unit): Fate[X, L, R] = { | |
| new FateImpl(underlying) | |
| } | |
| } | |
| private class FateImpl[X, L, R]( | |
| underlying: X => (Either[L, R] => Unit) => Unit) extends Fate[X, L, R]{ | |
| override def map[R2](f: R => R2): Fate[X, L, R2] = new FateImpl[X, L, R2]( | |
| context => g => underlying(context){ | |
| case Right(right) => g(Right(f(right))) | |
| case Left(left) => g(Left(left)) | |
| } | |
| ) | |
| override def flatMap[L2 >: L, R2](f: R => Fate[X, L2, R2]): Fate[X, L2, R2] = new FateImpl[X, L2, R2]( | |
| context => g => underlying(context){ | |
| case Right(right) => f(right).run(context)(g) | |
| case Left(left) => g(Left(left)) | |
| } | |
| ) | |
| override def run[L2 >: L, R2 >: R] = { | |
| underlying | |
| } | |
| } |
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 org.scalatest.{FlatSpecLike, Matchers} | |
| import x7c1.wheat.modern.kinds.RichFate.ToRichFate | |
| import scala.concurrent.ExecutionContext | |
| import scala.concurrent.duration.DurationInt | |
| class FateTest extends FlatSpecLike with Matchers { | |
| val provide = FutureFate.hold[CustomContext, CustomError] | |
| val context = CustomContext(ExecutionContext.global) | |
| it can "compose by for-yield" in { | |
| val fate = for { | |
| n1 <- provide right 1 | |
| n2 <- provide right 2 | |
| n3 <- provide right 3 | |
| } yield { | |
| n1 + n2 + n3 | |
| } | |
| fate.testRun(context){ | |
| case Left(error) => fail(error.message) | |
| case Right(n) => n shouldBe 6 | |
| } | |
| /* | |
| //cannot compile | |
| fate.run(CustomContextBoo(ExecutionContext.global)) | |
| // */ | |
| } | |
| it should "stop when exception thrown" in { | |
| val fate = for { | |
| n1 <- provide right 1 | |
| n2 <- provide right { throw new Exception("boo") } | |
| n3 <- provide right 3 | |
| } yield { | |
| n1 + n2 + n3 | |
| } | |
| fate.testRun(context){ | |
| case Left(error) => | |
| error.message shouldBe "boo" | |
| case Right(n) => | |
| fail(s"invalid response: $n") | |
| } | |
| } | |
| it can "await given msec" in { | |
| val fate = for { | |
| start <- provide right System.currentTimeMillis() | |
| n1 <- provide right 1 | |
| _ <- provide await 111.millis | |
| n2 <- provide right 2 | |
| _ <- provide await 222.millis | |
| n3 <- provide right 3 | |
| } yield { | |
| val elapsed = System.currentTimeMillis() - start | |
| elapsed.toInt -> (n1 + n2 + n3) | |
| } | |
| fate.testRun(context){ | |
| case Right((elapsed, sum)) => | |
| sum shouldBe 6 | |
| elapsed should be >= 333 | |
| case Left(error) => | |
| fail(error.message) | |
| } | |
| } | |
| } |
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 x7c1.wheat.macros.reify.HasConstructor | |
| import x7c1.wheat.modern.chrono.HasTimer | |
| import x7c1.wheat.modern.patch.TimerTask | |
| import scala.concurrent.duration.FiniteDuration | |
| import scala.concurrent.{ExecutionContext, Future} | |
| object FutureFate { | |
| type ErrorLike[X] = HasConstructor[Throwable => X] | |
| type HasContext[X] = X => ExecutionContext | |
| class AppliedHolder[X: HasContext, L: ErrorLike]{ | |
| def apply[R](f: => Either[L, R]): Fate[X, L, R] = | |
| Fate { x => g => | |
| implicit val context = implicitly[HasContext[X]] apply x | |
| Future(f) recover { | |
| case e => Left(implicitly[ErrorLike[L]] newInstance e) | |
| } map g | |
| } | |
| def right[A](f: => A): Fate[X, L, A] = { | |
| apply(Right(f)) | |
| } | |
| def await(duration: FiniteDuration)(implicit i: HasTimer[X]): Fate[X, L, Unit] = | |
| Fate { x => g => | |
| val task = TimerTask { | |
| g(Right({})) | |
| } | |
| i.timer.schedule(task, duration.toMillis) | |
| } | |
| } | |
| def hold[X: HasContext, L: ErrorLike]: AppliedHolder[X, L] = new AppliedHolder | |
| } |
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 concurrent.duration._ | |
| import scala.concurrent.{Await, Future, Promise} | |
| object RichFate { | |
| implicit class ToRichFate[X, L, R](fate: Fate[X, L, R]){ | |
| def toFuture(context: X): Future[Either[L, R]] = { | |
| val promise = Promise[Either[L, R]]() | |
| fate.run(context){ either => | |
| try promise trySuccess either | |
| catch { case e: Throwable => promise tryFailure e } | |
| } | |
| promise.future | |
| } | |
| def testRun(context: X)(f: Either[L, R] => Unit): Unit = { | |
| val either = Await.result(toFuture(context), atMost = 5.seconds) | |
| f(either) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment