From 837c1a12d9bcf88e3d1055de7e1673a1b92bbe21 Mon Sep 17 00:00:00 2001 From: adamw Date: Thu, 16 Nov 2017 12:22:50 +0100 Subject: Add the possibility for monads to recover from errors --- .../cats/AsyncHttpClientCatsBackend.scala | 4 ++++ .../asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala | 4 ++++ .../monix/AsyncHttpClientMonixBackend.scala | 4 ++++ .../scalaz/AsyncHttpClientScalazBackend.scala | 3 +++ .../main/scala/com/softwaremill/sttp/MonadError.scala | 16 ++++++++++++++++ .../sttp/okhttp/monix/OkHttpMonixBackend.scala | 5 +++++ 6 files changed, 36 insertions(+) diff --git a/async-http-client-backend/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala b/async-http-client-backend/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala index a38e08b..471d26c 100644 --- a/async-http-client-backend/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala +++ b/async-http-client-backend/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala @@ -78,4 +78,8 @@ private[cats] class AsyncMonad[F[_]](implicit F: Async[F]) F.flatMap(fa)(f) override def error[T](t: Throwable): F[T] = F.raiseError(t) + + override protected def handleWrappedError[T](rt: F[T])( + h: PartialFunction[Throwable, F[T]]): F[T] = + F.recoverWith(rt)(h) } diff --git a/async-http-client-backend/fs2/src/main/scala/com/softwaremill/sttp/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala b/async-http-client-backend/fs2/src/main/scala/com/softwaremill/sttp/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala index 4dfc1da..8343a9b 100644 --- a/async-http-client-backend/fs2/src/main/scala/com/softwaremill/sttp/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala +++ b/async-http-client-backend/fs2/src/main/scala/com/softwaremill/sttp/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala @@ -108,4 +108,8 @@ private[fs2] class EffectMonad[F[_]](implicit F: Effect[F]) F.flatMap(fa)(f) override def error[T](t: Throwable): F[T] = F.raiseError(t) + + override protected def handleWrappedError[T](rt: F[T])( + h: PartialFunction[Throwable, F[T]]): F[T] = + F.recoverWith(rt)(h) } diff --git a/async-http-client-backend/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/AsyncHttpClientMonixBackend.scala b/async-http-client-backend/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/AsyncHttpClientMonixBackend.scala index 9cb44f2..04df4ed 100644 --- a/async-http-client-backend/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/AsyncHttpClientMonixBackend.scala +++ b/async-http-client-backend/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/AsyncHttpClientMonixBackend.scala @@ -108,4 +108,8 @@ private[monix] object TaskMonad extends MonadAsyncError[Task] { } override def error[T](t: Throwable): Task[T] = Task.raiseError(t) + + override protected def handleWrappedError[T](rt: Task[T])( + h: PartialFunction[Throwable, Task[T]]): Task[T] = + rt.onErrorRecoverWith(h) } diff --git a/async-http-client-backend/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/AsyncHttpClientScalazBackend.scala b/async-http-client-backend/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/AsyncHttpClientScalazBackend.scala index 57e9577..3828233 100644 --- a/async-http-client-backend/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/AsyncHttpClientScalazBackend.scala +++ b/async-http-client-backend/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/AsyncHttpClientScalazBackend.scala @@ -74,4 +74,7 @@ private[scalaz] object TaskMonad extends MonadAsyncError[Task] { } override def error[T](t: Throwable): Task[T] = Task.fail(t) + + override protected def handleWrappedError[T](rt: Task[T])( + h: PartialFunction[Throwable, Task[T]]): Task[T] = rt.handleWith(h) } diff --git a/core/src/main/scala/com/softwaremill/sttp/MonadError.scala b/core/src/main/scala/com/softwaremill/sttp/MonadError.scala index 34edb0a..77c6235 100644 --- a/core/src/main/scala/com/softwaremill/sttp/MonadError.scala +++ b/core/src/main/scala/com/softwaremill/sttp/MonadError.scala @@ -8,7 +8,17 @@ trait MonadError[R[_]] { def unit[T](t: T): R[T] def map[T, T2](fa: R[T])(f: T => T2): R[T2] def flatMap[T, T2](fa: R[T])(f: T => R[T2]): R[T2] + def error[T](t: Throwable): R[T] + protected def handleWrappedError[T](rt: R[T])( + h: PartialFunction[Throwable, R[T]]): R[T] + def handleError[T](rt: => R[T])(h: PartialFunction[Throwable, R[T]]): R[T] = { + Try(rt) match { + case Success(v) => handleWrappedError(v)(h) + case Failure(e) if h.isDefinedAt(e) => h(e) + case Failure(e) => error(e) + } + } def flatten[T](ffa: R[R[T]]): R[T] = flatMap[R[T], T](ffa)(identity) @@ -26,7 +36,10 @@ object IdMonad extends MonadError[Id] { override def unit[T](t: T): Id[T] = t override def map[T, T2](fa: Id[T])(f: (T) => T2): Id[T2] = f(fa) override def flatMap[T, T2](fa: Id[T])(f: (T) => Id[T2]): Id[T2] = f(fa) + override def error[T](t: Throwable): Id[T] = throw t + override protected def handleWrappedError[T](rt: Id[T])( + h: PartialFunction[Throwable, Id[T]]): Id[T] = rt } class FutureMonad(implicit ec: ExecutionContext) @@ -36,7 +49,10 @@ class FutureMonad(implicit ec: ExecutionContext) override def map[T, T2](fa: Future[T])(f: (T) => T2): Future[T2] = fa.map(f) override def flatMap[T, T2](fa: Future[T])(f: (T) => Future[T2]): Future[T2] = fa.flatMap(f) + override def error[T](t: Throwable): Future[T] = Future.failed(t) + override protected def handleWrappedError[T](rt: Future[T])( + h: PartialFunction[Throwable, Future[T]]): Future[T] = rt.recoverWith(h) override def async[T]( register: ((Either[Throwable, T]) => Unit) => Unit): Future[T] = { diff --git a/okhttp-backend/monix/src/main/scala/com/softwaremill/sttp/okhttp/monix/OkHttpMonixBackend.scala b/okhttp-backend/monix/src/main/scala/com/softwaremill/sttp/okhttp/monix/OkHttpMonixBackend.scala index e85d271..f579ff5 100644 --- a/okhttp-backend/monix/src/main/scala/com/softwaremill/sttp/okhttp/monix/OkHttpMonixBackend.scala +++ b/okhttp-backend/monix/src/main/scala/com/softwaremill/sttp/okhttp/monix/OkHttpMonixBackend.scala @@ -117,4 +117,9 @@ private[monix] object TaskMonad extends MonadAsyncError[Task] { } override def error[T](t: Throwable): Task[T] = Task.raiseError(t) + + override protected def handleWrappedError[T](rt: Task[T])( + h: PartialFunction[Throwable, Task[T]]): Task[T] = rt.onErrorRecoverWith { + case t => h(t) + } } -- cgit v1.2.3