Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution to #109 #110

Merged
merged 10 commits into from
Feb 5, 2020
60 changes: 40 additions & 20 deletions core/src/main/scala/tofu/Errors.scala
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
package tofu

import cats.data.ReaderT
import cats.data.{EitherT, OptionT, ReaderT}
import cats.syntax.either._
import cats.{Applicative, ApplicativeError, Functor, Id}
import errorInstances._
import internal.{CachedMatcher, DataEffectComp}
import cats.{Applicative, ApplicativeError, Functor, Id, Monad}
import tofu.errorInstances._
import tofu.internal.{CachedMatcher, DataEffectComp}
import tofu.lift.Lift
import tofu.optics.{Downcast, Subset, Upcast}

import scala.reflect.ClassTag
import cats.data.EitherT
import cats.Monad
import cats.data.OptionT
import tofu.lift.Lift

trait Raise[F[_], E] extends Raise.ContravariantRaise[F, E] {
def raise[A](err: E): F[A]
}

object Raise extends RaiseInstances with DataEffectComp[Raise] with ErrorsInstanceChain[Raise] {

trait ContravariantRaise[F[_], -E] {
def raise[A](err: E): F[A]
}

}

sealed class RaiseInstances {
Expand All @@ -41,31 +40,32 @@ trait Restore[F[_]] extends RestoreTo[F, F] {
trait HandleTo[F[_], G[_], E] extends RestoreTo[F, G] {
def handleWith[A](fa: F[A])(f: E => G[A]): G[A]

def handle[A](fa: F[A])(f: E => A)(implicit G: Applicative[G]): G[A] =
handleWith(fa)(e => G.pure(f(e)))

def attempt[A](fa: F[A])(implicit F: Functor[F], G: Applicative[G]): G[Either[E, A]] =
handle(F.map(fa)(_.asRight[E]))(_.asLeft)

def handle[A](fa: F[A])(f: E => A)(implicit G: Applicative[G]): G[A] =
handleWith(fa)(e => G.pure(f(e)))
}

object HandleTo extends ErrorsToInstanceChain[HandleTo]

trait Handle[F[_], E] extends HandleTo[F, F, E] with Restore[F] {

def tryHandleWith[A](fa: F[A])(f: E => Option[F[A]]): F[A]

def recover[A](fa: F[A])(pf: PartialFunction[E, A])(implicit F: Applicative[F]): F[A] =
tryHandle(fa)(pf.lift)

def tryHandle[A](fa: F[A])(f: E => Option[A])(implicit F: Applicative[F]): F[A] =
tryHandleWith(fa)(e => f(e).map(F.pure))

def handleWith[A](fa: F[A])(f: E => F[A]): F[A] =
tryHandleWith(fa)(e => Some(f(e)))

def recoverWith[A](fa: F[A])(pf: PartialFunction[E, F[A]]): F[A] =
tryHandleWith(fa)(pf.lift)

def recover[A](fa: F[A])(pf: PartialFunction[E, A])(implicit F: Applicative[F]): F[A] =
tryHandle(fa)(pf.lift)

def restoreWith[A](fa: F[A])(ra: => F[A]): F[A] = handleWith(fa)(_ => ra)

def handleWith[A](fa: F[A])(f: E => F[A]): F[A] =
tryHandleWith(fa)(e => Some(f(e)))
}

object Handle extends HandleInstances with DataEffectComp[Handle] with ErrorsInstanceChain[Handle] {
Expand All @@ -75,7 +75,10 @@ object Handle extends HandleInstances with DataEffectComp[Handle] with ErrorsIns

def tryHandleWith[A](fa: F[A])(f: E => Option[F[A]]): F[A] =
recWith(fa)(CachedMatcher(f))
override def recoverWith[A](fa: F[A])(pf: PartialFunction[E, F[A]]): F[A] = recWith(fa)(pf)

override def recoverWith[A](fa: F[A])(pf: PartialFunction[E, F[A]]): F[A] =
recWith(fa)(pf)

override def recover[A](fa: F[A])(pf: PartialFunction[E, A])(implicit F: Applicative[F]): F[A] =
recWith(fa)(pf andThen F.pure _)
}
Expand All @@ -94,10 +97,12 @@ trait ErrorsToInstanceChain[TC[f[_], g[_], e] >: ErrorsTo[f, g, e]]
extends ErrorsInstanceChain[Lambda[(f[_], e) => TC[f, f, e]]] {
final implicit def eitherTIntance[F[_], E](implicit F: Monad[F]): TC[EitherT[F, E, *], F, E] =
new EitherTErrorsTo[F, E]

final implicit def optionTIntance[F[_]](implicit F: Monad[F]): TC[OptionT[F, *], F, Unit] = new OptionTErrorsTo[F]

final implicit def eitherIntance[E]: TC[Either[E, *], Id, E] = new EitherErrorsTo[E]
final implicit val optionTIntance: TC[Option, Id, Unit] = OptionErrorsTo
final implicit def eitherIntance[E]: TC[Either[E, *], Id, E] =
new EitherErrorsTo[E]
final implicit val optionTIntance: TC[Option, Id, Unit] = OptionErrorsTo
}

trait Errors[F[_], E] extends Raise[F, E] with Handle[F, E] with ErrorsTo[F, F, E]
Expand All @@ -117,10 +122,25 @@ trait ErrorInstances {
new Errors[ReaderT[F, R, *], E] {
def raise[A](err: E): ReaderT[F, R, A] =
ReaderT.liftF(F.raise(err))

def tryHandleWith[A](fa: ReaderT[F, R, A])(f: E => Option[ReaderT[F, R, A]]): ReaderT[F, R, A] =
ReaderT(r => F.tryHandleWith(fa.run(r))(e => f(e).map(_.run(r))))

def restore[A](fa: ReaderT[F, R, A]): ReaderT[F, R, Option[A]] =
ReaderT(r => F.restore(fa.run(r)))

def lift[A](fa: ReaderT[F, R, A]): ReaderT[F, R, A] = fa
}
}

object HasContext {
def apply[F[_], C](implicit hc: HasContext[F, C]): HasContext[F, C] = hc
}

object HasLocal {
def apply[F[_], C](implicit hl: HasLocal[F, C]): HasLocal[F, C] = hl
}

object HasContextRun {
def apply[F[_], G[_], C](implicit hcr: HasContextRun[F, G, C]): HasContextRun[F, G, C] = hcr
}
7 changes: 5 additions & 2 deletions core/src/main/scala/tofu/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import cats.effect.Bracket
import cats.{ApplicativeError, MonadError}

package object tofu {
type HasContext[F[_], C] = Context[F] { type Ctx = C }
type HasLocal[F[_], C] = Local[F] { type Ctx = C }
type HasContext[F[_], C] = Context[F] { type Ctx = C }

type HasLocal[F[_], C] = Local[F] { type Ctx = C }

type HasContextRun[F[_], G[_], C] = RunContext[F] { type Ctx = C; type Lower[A] = G[A] }

type ApplicativeThrow[F[_]] = ApplicativeError[F, Throwable]
type MonadThrow[F[_]] = MonadError[F, Throwable]
type BracketThrow[F[_]] = Bracket[F, Throwable]

type TConst[A, B] = A

}