Last active
March 16, 2018 17:43
-
-
Save d6y/ec78acb17d8bfee773bcc40d8151488a to your computer and use it in GitHub Desktop.
Assuming you're stuck using Future somewhere, well...
This file contains 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
package example | |
import scala.language.higherKinds | |
import cats.MonadError | |
import cats.syntax.functor._ | |
// Both Try and Future are backed by Exceptions | |
// We can trade Future for Try in tests to avoid concurrency and execution context etc | |
case class Value(isPrime: Boolean) | |
abstract class Client[F[_]](implicit mE: MonadError[F, Throwable]) { | |
def checkPrimeness(n: Int): F[Value] | |
} | |
case class API[F[_]](client: Client[F])(implicit mE: MonadError[F, Throwable]) { | |
// The API uses a client, and does some computation with the result from the client. | |
// In this case the "computation" is a trivial _.isPrime | |
def isPrime(n: Int): F[Boolean] = | |
client.checkPrimeness(n).map(_.isPrime) | |
} | |
object Application extends App { | |
import scala.concurrent.Future | |
import cats.instances.future._ | |
import scala.concurrent.ExecutionContext.Implicits.global | |
// Partial implementation: only knows how to deal with the number 7 | |
object NetworkClient extends Client[Future] { | |
val mE = implicitly[MonadError[Future, Throwable]] | |
def checkPrimeness(n: Int): Future[Value] = | |
if (n == 7) mE.pure(Value(true)) | |
else mE.raiseError(new IllegalArgumentException(s"Not implemented for $n")) | |
} | |
val f7: Future[Boolean] = API(NetworkClient).isPrime(7) | |
val f8: Future[Boolean] = API(NetworkClient).isPrime(8) | |
import scala.concurrent._ | |
import scala.concurrent.duration._ | |
println(Await.result(f7, 1.seconds)) | |
// println(Await.result(f8, 1.seconds)) | |
// But also... | |
Test.test() | |
} | |
object Test { | |
import scala.util.{Success, Try} | |
import cats.instances.try_._ | |
case class ConstantClient(result: Try[Value]) extends Client[Try] { | |
def checkPrimeness(n: Int): Try[Value] = result | |
} | |
def test(): Unit = { | |
val client = ConstantClient(Success(Value(true))) | |
assert( API(client).isPrime(99) == Success(true) ) | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You gain some minor flexibility if you move the
MonadError
implicit to the methods instead of the the class.