Skip to content

Instantly share code, notes, and snippets.

View dkomanov's full-sized avatar

Dmitry Komanov dkomanov

View GitHub Profile
@dkomanov
dkomanov / safeFuture.scala
Created May 23, 2020 22:43
[writing-async-app-in-scala-part-2] safeFuture implementation
def safeFuture[T](f: => Future[T]): Future[T] = {
try {
f
} catch {
case NonFatal(e) => Future.failed(e)
}
}
def handleFromMemcached(r: HttpRequest): Future[Option[Int]] = safeFuture {
require(r.uri != "/memcached-boom", "Memcached Boom!")
@dkomanov
dkomanov / executeLazily.scala
Created May 23, 2020 22:36
[writing-async-app-in-scala-part-2] executeLazily implementation
def executeLazily[Argument, Return](argument: Argument,
list: List[Argument => Future[Option[Return]]])
(implicit ec: ExecutionContext): Future[Option[Return]] = {
val promise = Promise[Option[Return]]()
val iterator = list.iterator
def completeWith(t: Try[Option[Return]]): Unit = t match {
case Success(value) =>
if (value.isDefined || !iterator.hasNext)
promise.success(value)
@dkomanov
dkomanov / RpcCaching2.scala
Created May 23, 2020 22:34
[writing-async-app-in-scala-part-2] RPC with caching 2
val handlers: List[HttpRequest => Future[Option[Int]]] = List(
handleFromMemcached,
handleFromCdn,
r => handleFromHadoop(r).map(Some.apply)
)
def handle(r: HttpRequest): Future[Int] = {
executeLazily(handlers.map(handler => () => handler(r)))
.map(_.getOrElse(throw new IllegalStateException("Hadoop should always return Some!")))
}
@dkomanov
dkomanov / RpcCaching1.scala
Created May 23, 2020 22:31
[writing-async-app-in-scala-part-2] RPC with caching
case class HttpRequest(uri: String)
def handleFromMemcached(r: HttpRequest): Future[Option[Int]] = {
require(r.uri != "/memcached-boom", "Memcached Boom!")
Future.successful(if (r.uri == "/memcached") Some(42) else None)
}
def handleFromCdn(r: HttpRequest): Future[Option[Int]] = {
require(r.uri != "/cnd-boom", "CDN Boom!")
Future.successful(if (r.uri == "/cdn") Some(42) else None)
@dkomanov
dkomanov / FutureException2.scala
Created May 23, 2020 22:29
[writing-async-app-in-scala-part-2] Unsafe Exception Propagation
def innocentFunction(param: AnyRef): Future[Int] = {
require(param != null)
Future.successful(42)
}
innocentFunction(null)
.map(_ => throw new IllegalStateException)
// an IllegalArgumentException will be thrown before map call
@dkomanov
dkomanov / FutureException1.scala
Created May 23, 2020 22:28
[writing-async-app-in-scala-part-2] Safe Exception Propagation
Future(42)
.map[Int](_ => throw new IllegalArgumentException)
.flatMap[Int](_ => throw new IllegalStateException)
// Effectively: Future.failed(new IllegalArgumentException)
@dkomanov
dkomanov / RecoverFilter.scala
Created May 22, 2020 22:52
[writing-async-app-in-scala-part-1] recoverFilter
implicit class FutureExtensions[T](val future: Future[T]) extends AnyVal {
def recoverFilter(f: => T): Future[T] =
future.recover {
case _: NoSuchElementException | ControlException => f
}
def recoverFilterWith(f: => Future[T]): Future[T] =
future.recoverWith {
case _: NoSuchElementException | ControlException => f
}
@dkomanov
dkomanov / Option2.scala
Created May 22, 2020 22:25
[writing-async-app-in-scala-part-1] option 2
for {
movie <- findMovie("Dark Waters") orFail new MovieNotFoundException("...")
} yield movie
@dkomanov
dkomanov / Option1.scala
Last active May 31, 2020 11:38
[writing-async-app-in-scala-part-1] option 1
implicit class FutureOfOptionExtensions[T](val v: Future[Option[T]]) extends AnyVal {
def orFail(e: => Throwable): Future[T] =
v.flatMap(_.fold[Future[T]](Future.failed(e), Future.successful)
}
@dkomanov
dkomanov / ForComprehensions7.scala
Created May 22, 2020 22:19
[writing-async-app-in-scala-part-1] for-comprehensions 7
for {
_ <- hasPermissions orFail new PermissionDeniedException("...")
movie <- getMovie.filterOrFail(!_.hidden, new HiddenMovieException("..."))
actors <- getActors(movie)
plot <- getPlot(movie)
} yield MovieDescription(movie, actors, plot)