All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cats.data.IorT.scala Maven / Gradle / Ivy

The newest version!
package cats
package data

import cats.arrow.FunctionK
import cats.syntax.either._
import cats.syntax.option._

final case class IorT[F[_], A, B](value: F[Ior[A, B]]) {

  def fold[C](fa: A => C, fb: B => C, fab: (A, B) => C)(implicit F: Functor[F]): F[C] =
    F.map(value)(_.fold(fa, fb, fab))

  def isLeft(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.isLeft)

  def isRight(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.isRight)

  def isBoth(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.isBoth)

  def swap(implicit F: Functor[F]): IorT[F, B, A] = IorT(F.map(value)(_.swap))

  def getOrElse[BB >: B](default: => BB)(implicit F: Functor[F]): F[BB] = F.map(value)(_.getOrElse(default))

  def getOrElseF[BB >: B](default: => F[BB])(implicit F: Monad[F]): F[BB] =
    F.flatMap(value) {
      case Ior.Left(_)    => default
      case Ior.Right(b)   => F.pure(b)
      case Ior.Both(_, b) => F.pure(b)
    }

  def valueOr[BB >: B](f: A => BB)(implicit F: Functor[F], BB: Semigroup[BB]): F[BB] = F.map(value)(_.valueOr(f))

  def forall(f: B => Boolean)(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.forall(f))

  def exists(f: B => Boolean)(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.exists(f))

  def toOption(implicit F: Functor[F]): OptionT[F, B] = OptionT(F.map(value)(_.toOption))

  def toEither(implicit F: Functor[F]): EitherT[F, A, B] = EitherT(F.map(value)(_.toEither))

  def toNested: Nested[F, Ior[A, *], B] = Nested[F, Ior[A, *], B](value)

  def toNestedValidated(implicit F: Functor[F]): Nested[F, Validated[A, *], B] =
    Nested[F, Validated[A, *], B](F.map(value)(_.toValidated))

  def toValidated(implicit F: Functor[F]): F[Validated[A, B]] = F.map(value)(_.toValidated)

  def to[G[_]](implicit F: Functor[F], G: Alternative[G]): F[G[B]] = F.map(value)(_.to[G, B])

  def collectRight(implicit FA: Alternative[F], FM: FlatMap[F]): F[B] = FM.flatMap(value)(_.to[F, B])

  def merge[AA >: A](implicit ev: B <:< AA, F: Functor[F], AA: Semigroup[AA]): F[AA] = F.map(value)(_.merge(ev, AA))

  def show(implicit show: Show[F[Ior[A, B]]]): String = show.show(value)

  def map[D](f: B => D)(implicit F: Functor[F]): IorT[F, A, D] = IorT(F.map(value)(_.map(f)))

  def mapK[G[_]](f: F ~> G): IorT[G, A, B] = IorT[G, A, B](f(value))

  def bimap[C, D](fa: A => C, fb: B => D)(implicit F: Functor[F]): IorT[F, C, D] = IorT(F.map(value)(_.bimap(fa, fb)))

  def leftMap[C](f: A => C)(implicit F: Functor[F]): IorT[F, C, B] = IorT(F.map(value)(_.leftMap(f)))

  def leftFlatMap[BB >: B, C](f: A => IorT[F, C, BB])(implicit F: Monad[F], BB: Semigroup[BB]): IorT[F, C, BB] =
    IorT(F.flatMap(value) {
      case Ior.Left(a)      => f(a).value
      case r @ Ior.Right(_) => F.pure(r.asInstanceOf[Ior[C, BB]])
      case Ior.Both(a, b) =>
        F.map(f(a).value) {
          case Ior.Left(c)     => Ior.Both(c, b)
          case Ior.Right(b1)   => Ior.Right(BB.combine(b, b1))
          case Ior.Both(c, b1) => Ior.Both(c, BB.combine(b, b1))
        }
    })

  def leftSemiflatMap[C](f: A => F[C])(implicit F: Monad[F]): IorT[F, C, B] =
    IorT(F.flatMap(value) {
      case Ior.Left(a)      => F.map(f(a))(Ior.Left(_))
      case r @ Ior.Right(_) => F.pure(r.asInstanceOf[Ior[C, B]])
      case Ior.Both(a, b)   => F.map(f(a))(Ior.Both(_, b))
    })

  def transform[C, D](f: Ior[A, B] => Ior[C, D])(implicit F: Functor[F]): IorT[F, C, D] = IorT(F.map(value)(f))

  def applyAlt[D](ff: IorT[F, A, B => D])(implicit F: Apply[F], A: Semigroup[A]): IorT[F, A, D] =
    IorT[F, A, D](F.map2(value, ff.value)((iorb, iorbd) => Apply[Ior[A, *]].ap(iorbd)(iorb)))

  def flatMap[AA >: A, D](f: B => IorT[F, AA, D])(implicit F: Monad[F], AA: Semigroup[AA]): IorT[F, AA, D] =
    IorT(F.flatMap(value) {
      case l @ Ior.Left(_) => F.pure(l.asInstanceOf[Ior[AA, D]])
      case Ior.Right(b)    => f(b).value
      case Ior.Both(a, b) =>
        F.map(f(b).value) {
          case Ior.Left(a1)    => Ior.Left(AA.combine(a, a1))
          case Ior.Right(d)    => Ior.Both(a, d)
          case Ior.Both(a1, d) => Ior.Both(AA.combine(a, a1), d)
        }
    })

  def flatMapF[AA >: A, D](f: B => F[Ior[AA, D]])(implicit F: Monad[F], AA: Semigroup[AA]): IorT[F, AA, D] =
    flatMap(b => IorT(f(b)))

  def subflatMap[AA >: A, D](f: B => Ior[AA, D])(implicit F: Functor[F], AA: Semigroup[AA]): IorT[F, AA, D] =
    IorT(F.map(value)(_.flatMap(f)))

  def semiflatMap[D](f: B => F[D])(implicit F: Monad[F]): IorT[F, A, D] =
    IorT(F.flatMap(value) {
      case l @ Ior.Left(_) => F.pure(l.asInstanceOf[Ior[A, D]])
      case Ior.Right(b)    => F.map(f(b))(Ior.right)
      case Ior.Both(a, b)  => F.map(f(b))(Ior.both(a, _))
    })

  def traverse[G[_], D](f: B => G[D])(implicit traverseF: Traverse[F], applicativeG: Applicative[G]): G[IorT[F, A, D]] =
    applicativeG.map(traverseF.traverse(value)(ior => Traverse[Ior[A, *]].traverse(ior)(f)))(IorT.apply)

  def foldLeft[C](c: C)(f: (C, B) => C)(implicit F: Foldable[F]): C =
    F.foldLeft(value, c)((c, ior) => ior.foldLeft(c)(f))

  def foldRight[C](lc: Eval[C])(f: (B, Eval[C]) => Eval[C])(implicit F: Foldable[F]): Eval[C] =
    F.foldRight(value, lc)((ior, lc) => ior.foldRight(lc)(f))

  def ===(that: IorT[F, A, B])(implicit eq: Eq[F[Ior[A, B]]]): Boolean =
    eq.eqv(value, that.value)

  def combine(that: IorT[F, A, B])(implicit F: Apply[F], A: Semigroup[A], B: Semigroup[B]): IorT[F, A, B] =
    IorT(F.map2(this.value, that.value)(_.combine(_)))
}

object IorT extends IorTInstances {

  /**
   * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics.
   */
  final private[data] class LeftPartiallyApplied[B](private val dummy: Boolean = true) extends AnyVal {
    def apply[F[_], A](fa: F[A])(implicit F: Functor[F]): IorT[F, A, B] = IorT(F.map(fa)(Ior.left))
  }

  /**
   * Creates a left version of `IorT[F, A, B]` from a `F[A]`
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> IorT.left[Int](Option("err"))
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Left(err)))
   * }}}
   */
  final def left[B]: LeftPartiallyApplied[B] = new LeftPartiallyApplied[B]

  /**
   * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics.
   */
  final private[data] class LeftTPartiallyApplied[F[_], B](private val dummy: Boolean = true) extends AnyVal {
    def apply[A](a: A)(implicit F: Applicative[F]): IorT[F, A, B] = IorT(F.pure(Ior.left(a)))
  }

  /**
   * Creates a left version of `IorT[F, A, B]` from a `A`
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> IorT.leftT[Option, Int]("err")
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Left(err)))

   * }}}
   */
  final def leftT[F[_], B]: LeftTPartiallyApplied[F, B] = new LeftTPartiallyApplied[F, B]

  /**
   * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics.
   */
  final private[data] class RightPartiallyApplied[A](private val dummy: Boolean = true) extends AnyVal {
    def apply[F[_], B](fb: F[B])(implicit F: Functor[F]): IorT[F, A, B] = IorT(F.map(fb)(Ior.right))
  }

  /**
   * Creates a right version of `IorT[F, A, B]` from a `F[B]`
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> IorT.right[String](Option(3))
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Right(3)))
   * }}}
   */
  final def right[A]: RightPartiallyApplied[A] = new RightPartiallyApplied[A]

  /**
   * Alias for [[pure]]
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> IorT.rightT[Option, String](3)
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Right(3)))
   * }}}
   */
  final def rightT[F[_], A]: PurePartiallyApplied[F, A] = pure

  /**
   * Creates a both version of `IorT[F, A, B]` from a `F[A]` and a `F[B]`
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> IorT.both(Option("err"), Option(3))
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Both(err,3)))
   * }}}
   */
  final def both[F[_], A, B](fa: F[A], fb: F[B])(implicit F: Apply[F]): IorT[F, A, B] =
    IorT(F.map2(fa, fb)((a, b) => Ior.Both(a, b)))

  /**
   * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics.
   */
  final private[data] class BothTPartiallyApplied[F[_]](private val dummy: Boolean = true) extends AnyVal {
    def apply[A, B](a: A, b: B)(implicit F: Applicative[F]): IorT[F, A, B] = IorT(F.pure(Ior.Both(a, b)))
  }

  /**
   * Creates a both version of `IorT[F, A, B]` from a `A` and a `B`
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> IorT.bothT[Option]("err", 3)
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Both(err,3)))
   * }}}
   */
  final def bothT[F[_]]: BothTPartiallyApplied[F] = new BothTPartiallyApplied[F]

  /**
   * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics.
   */
  final private[data] class PurePartiallyApplied[F[_], A](private val dummy: Boolean = true) extends AnyVal {
    def apply[B](b: B)(implicit F: Applicative[F]): IorT[F, A, B] = IorT(F.pure(Ior.right(b)))
  }

  /**
   * Creates a right version of `IorT[F, A, B]` from a `B`
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> IorT.pure[Option, String](3)
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Right(3)))
   * }}}
   */
  final def pure[F[_], A]: PurePartiallyApplied[F, A] = new PurePartiallyApplied[F, A]

  /**
   * Alias for [[right]]
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> val o: Option[Int] = Some(3)
   * scala> val n: Option[Int] = None
   * scala> IorT.liftF(o)
   * res0: cats.data.IorT[Option,Nothing,Int] = IorT(Some(Right(3)))
   * scala> IorT.liftF(n)
   * res1: cats.data.IorT[Option,Nothing,Int] = IorT(None)
   * }}}
   */
  final def liftF[F[_], A, B](fb: F[B])(implicit F: Applicative[F]): IorT[F, A, B] = right(fb)

  /**
   * Same as [[liftF]], but expressed as a FunctionK for use with [[IorT.mapK]]
   * {{{
   * scala> import cats._, data._, implicits._
   * scala> val a: OptionT[Eval, Int] = 1.pure[OptionT[Eval, *]]
   * scala> val b: OptionT[IorT[Eval, String, *], Int] = a.mapK(IorT.liftK)
   * scala> b.value.value.value
   * res0: cats.data.Ior[String,Option[Int]] = Right(Some(1))
   * }}}
   */
  final def liftK[F[_], A](implicit F: Functor[F]): F ~> IorT[F, A, *] =
    new (F ~> IorT[F, A, *]) {
      def apply[B](fb: F[B]): IorT[F, A, B] = right(fb)
    }

  /**
   * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics.
   */
  final private[data] class FromIorPartiallyApplied[F[_]](private val dummy: Boolean = true) extends AnyVal {
    def apply[A, B](ior: Ior[A, B])(implicit F: Applicative[F]): IorT[F, A, B] = IorT(F.pure(ior))
  }

  /**
   * Transforms an `Ior` into an `IorT`, lifted into the specified `Applicative`.
   * {{{
   * scala> import cats.data.{IorT, Ior}
   * scala> import cats.implicits._
   * scala> val i: Ior[String, Int] = Ior.both("warning", 3)
   * scala> IorT.fromIor[Option](i)
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Both(warning,3)))
   * }}}
   */
  final def fromIor[F[_]]: FromIorPartiallyApplied[F] = new FromIorPartiallyApplied[F]

  /**
   * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics.
   */
  final private[data] class FromEitherPartiallyApplied[F[_]](private val dummy: Boolean = true) extends AnyVal {
    def apply[E, A](either: Either[E, A])(implicit F: Applicative[F]): IorT[F, E, A] = IorT(F.pure(either.toIor))
  }

  /**
   * Transforms an `Either` into an `IorT`, lifted into the specified `Applicative`.
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> val e: Either[String, Int] = Either.right(3)
   * scala> IorT.fromEither[Option](e)
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Right(3)))
   * }}}
   */
  final def fromEither[F[_]]: FromEitherPartiallyApplied[F] = new FromEitherPartiallyApplied[F]

  /**
   * Transforms an `F[Either]` into an `IorT`.
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> val e: Either[String, Int] = Either.right(3)
   * scala> IorT.fromEitherF(Option(e))
   * res0: cats.data.IorT[Option,String,Int] = IorT(Some(Right(3)))
   * }}}
   */
  final def fromEitherF[F[_], E, A](feither: F[Either[E, A]])(implicit F: Functor[F]): IorT[F, E, A] =
    IorT(F.map(feither)(_.toIor))

  /**
   * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics.
   */
  final private[data] class FromOptionPartiallyApplied[F[_]](private val dummy: Boolean = true) extends AnyVal {
    def apply[E, A](option: Option[A], ifNone: => E)(implicit F: Applicative[F]): IorT[F, E, A] =
      IorT(F.pure(option.toRightIor(ifNone)))
  }

  /**
   * Transforms an `Option` into an `IorT`, lifted into the specified `Applicative` and using
   * the second argument if the `Option` is a `None`.
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> val o: Option[Int] = None
   * scala> IorT.fromOption[List](o, "Answer not known.")
   * res0: cats.data.IorT[List,String,Int] = IorT(List(Left(Answer not known.)))
   * scala> IorT.fromOption[List](Some(42), "Answer not known.")
   * res1: cats.data.IorT[List,String,Int] = IorT(List(Right(42)))
   * }}}
   */
  final def fromOption[F[_]]: FromOptionPartiallyApplied[F] = new FromOptionPartiallyApplied[F]

  /**
   * Transforms an `F[Option]` into an `IorT`, using the second argument if the `Option` is a `None`.
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> val o: Option[Int] = None
   * scala> IorT.fromOptionF(List(o), "Answer not known.")
   * res0: cats.data.IorT[List,String,Int]  = IorT(List(Left(Answer not known.)))
   * scala> IorT.fromOptionF(List(Option(42)), "Answer not known.")
   * res1: cats.data.IorT[List,String,Int] = IorT(List(Right(42)))
   * }}}
   */
  final def fromOptionF[F[_], E, A](foption: F[Option[A]], ifNone: => E)(implicit F: Functor[F]): IorT[F, E, A] =
    IorT(F.map(foption)(_.toRightIor(ifNone)))

  /**
   * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics.
   */
  final private[data] class CondPartiallyApplied[F[_]](private val dummy: Boolean = true) extends AnyVal {
    def apply[A, B](test: Boolean, right: => B, left: => A)(implicit F: Applicative[F]): IorT[F, A, B] =
      IorT(F.pure(if (test) Ior.right(right) else Ior.left(left)))
  }

  /**
   * If the condition is satisfied, return the given `B` in `Ior.Right`, otherwise, return the given
   * `A` in `Ior.Left`, lifted into the specified `Applicative`.
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> val userInput = "hello world"
   * scala> IorT.cond[Option](
   *      |   userInput.forall(_.isDigit) && userInput.size == 10,
   *      |   userInput,
   *      |   "The input does not look like a phone number")
   * res0: cats.data.IorT[Option,String,String] = IorT(Some(Left(The input does not look like a phone number)))
   * }}}
   */
  final def cond[F[_]]: CondPartiallyApplied[F] = new CondPartiallyApplied[F]

  /**
   * If the condition is satisfied, return the value of `IorT.right` on `F[B]`, otherwise, return the
   * value of `IorT.left` on `F[A]`.
   * {{{
   * scala> import cats.data.IorT
   * scala> import cats.implicits._
   * scala> val userInput = "hello world"
   * scala> IorT.condF[Option, String, String](
   *      |   userInput.forall(_.isDigit) && userInput.size == 10,
   *      |   Some(userInput),
   *      |   None)
   * res0: cats.data.IorT[Option,String,String] = IorT(None)
   * }}}
   */
  final def condF[F[_], A, B](test: Boolean, right: => F[B], left: => F[A])(implicit F: Functor[F]): IorT[F, A, B] =
    IorT(if (test) F.map(right)(Ior.right) else F.map(left)(Ior.left))
}

abstract private[data] class IorTInstances extends IorTInstances1 {

  implicit def catsDataShowForIorT[F[_], A, B](implicit sh: Show[F[Ior[A, B]]]): Show[IorT[F, A, B]] =
    Contravariant[Show].contramap(sh)(_.value)

  implicit def catsDataBifunctorForIorT[F[_]](implicit F: Functor[F]): Bifunctor[IorT[F, *, *]] =
    new Bifunctor[IorT[F, *, *]] {
      override def bimap[A, B, C, D](iort: IorT[F, A, B])(fa: A => C, fb: B => D): IorT[F, C, D] = iort.bimap(fa, fb)
    }

  implicit def catsDataTraverseForIorT[F[_], A](implicit F: Traverse[F]): Traverse[IorT[F, A, *]] =
    new IorTTraverse[F, A] with IorTFunctor[F, A] { val F0: Traverse[F] = F }

  implicit def catsDataMonoidForIorT[F[_], A, B](implicit F: Monoid[F[Ior[A, B]]]): Monoid[IorT[F, A, B]] =
    new IorTMonoid[F, A, B] { val F0: Monoid[F[Ior[A, B]]] = F }

  implicit def catsDataParallelForIorTWithParallelEffect[M[_], E](
    implicit P: Parallel[M],
    E: Semigroup[E]
  ): Parallel.Aux[IorT[M, E, *], IorT[P.F, E, *]] { type Dummy } = new Parallel[IorT[M, E, *]] {
    type F[x] = IorT[P.F, E, x]
    type Dummy // fix to make this one more specific than the catsDataParallelForIorTWithSequentialEffect, see https://github.com/typelevel/cats/pull/2335#issuecomment-408249775

    val parallel: IorT[M, E, *] ~> IorT[P.F, E, *] =
      λ[IorT[M, E, *] ~> IorT[P.F, E, *]](fm => IorT(P.parallel(fm.value)))
    val sequential: IorT[P.F, E, *] ~> IorT[M, E, *] =
      λ[IorT[P.F, E, *] ~> IorT[M, E, *]](ff => IorT(P.sequential(ff.value)))

    private[this] val FA: Applicative[P.F] = P.applicative
    private[this] val IorA: Applicative[Ior[E, *]] = Parallel[Ior[E, *], Ior[E, *]].applicative

    val applicative: Applicative[IorT[P.F, E, *]] = new Applicative[IorT[P.F, E, *]] {
      def pure[A](a: A): IorT[P.F, E, A] = IorT.pure(a)(FA)
      def ap[A, B](ff: IorT[P.F, E, A => B])(fa: IorT[P.F, E, A]): IorT[P.F, E, B] =
        IorT(FA.map2(ff.value, fa.value)((f, a) => IorA.ap(f)(a)))
    }

    lazy val monad: Monad[IorT[M, E, *]] = {
      implicit def underlyingMonadM: Monad[M] = P.monad
      Monad[IorT[M, E, *]]
    }
  }

  implicit def catsDataDeferForIor[F[_], E](implicit F: Defer[F]): Defer[IorT[F, E, *]] =
    new Defer[IorT[F, E, *]] {
      def defer[A](fa: => IorT[F, E, A]): IorT[F, E, A] =
        IorT(F.defer(fa.value))
    }
}

abstract private[data] class IorTInstances1 extends IorTInstances2 {
  implicit def catsDataSemigroupForIorT[F[_], A, B](implicit F: Semigroup[F[Ior[A, B]]]): Semigroup[IorT[F, A, B]] =
    new IorTSemigroup[F, A, B] { val F0: Semigroup[F[Ior[A, B]]] = F }

  implicit def catsDataFoldableForIorT[F[_], A](implicit F: Foldable[F]): Foldable[IorT[F, A, *]] =
    new IorTFoldable[F, A] { val F0: Foldable[F] = F }

  implicit def catsDataMonadErrorForIorT[F[_], A](implicit F: Monad[F], A: Semigroup[A]): MonadError[IorT[F, A, *], A] =
    new IorTMonadError[F, A] {
      val A0: Semigroup[A] = A
      val F0: Monad[F] = F
    }

  implicit def catsDataParallelForIorTWithSequentialEffect[F0[_], E](
    implicit F: Monad[F0],
    E: Semigroup[E]
  ): Parallel.Aux[IorT[F0, E, *], IorT[F0, E, *]] = new Parallel[IorT[F0, E, *]] {
    type F[x] = IorT[F0, E, x]
    private[this] val identityK: IorT[F0, E, *] ~> IorT[F0, E, *] = FunctionK.id
    private[this] val underlyingParallel: Parallel.Aux[Ior[E, *], Ior[E, *]] =
      Ior.catsDataParallelForIor[E]

    def parallel: IorT[F0, E, *] ~> IorT[F0, E, *] = identityK
    def sequential: IorT[F0, E, *] ~> IorT[F0, E, *] = identityK

    val applicative: Applicative[IorT[F0, E, *]] = new Applicative[IorT[F0, E, *]] {
      def pure[A](a: A): IorT[F0, E, A] = IorT.pure(a)
      def ap[A, B](ff: IorT[F0, E, A => B])(fa: IorT[F0, E, A]): IorT[F0, E, B] =
        IorT(F.map2(ff.value, fa.value)((f, a) => underlyingParallel.applicative.ap[A, B](f)(a)))
    }

    lazy val monad: Monad[IorT[F0, E, *]] = Monad[IorT[F0, E, *]]
  }

}

abstract private[data] class IorTInstances2 extends IorTInstances3 {
  implicit def catsDataMonadErrorFForIorT[F[_], A, E](implicit FE: MonadError[F, E],
                                                      A: Semigroup[A]): MonadError[IorT[F, A, *], E] =
    new IorTMonadErrorF[F, A, E] {
      val A0: Semigroup[A] = A
      val F0: MonadError[F, E] = FE
    }

  implicit def catsDataEqForIorT[F[_], A, B](implicit F: Eq[F[Ior[A, B]]]): Eq[IorT[F, A, B]] =
    new IorTEq[F, A, B] { val F0: Eq[F[Ior[A, B]]] = F }
}

abstract private[data] class IorTInstances3 {
  implicit def catsDataFunctorForIorT[F[_], A](implicit F: Functor[F]): Functor[IorT[F, A, *]] =
    new IorTFunctor[F, A] { val F0: Functor[F] = F }
}

sealed private[data] trait IorTFunctor[F[_], A] extends Functor[IorT[F, A, *]] {
  implicit def F0: Functor[F]

  override def map[B, D](iort: IorT[F, A, B])(f: B => D): IorT[F, A, D] = iort.map(f)
}

sealed private[data] trait IorTEq[F[_], A, B] extends Eq[IorT[F, A, B]] {
  implicit def F0: Eq[F[Ior[A, B]]]

  override def eqv(x: IorT[F, A, B], y: IorT[F, A, B]): Boolean = x === y
}

sealed private[data] trait IorTMonad[F[_], A] extends Monad[IorT[F, A, *]] with IorTFunctor[F, A] {
  implicit def A0: Semigroup[A]
  implicit override def F0: Monad[F]

  override def pure[B](b: B): IorT[F, A, B] = IorT.pure(b)

  override def flatMap[B, D](iort: IorT[F, A, B])(f: B => IorT[F, A, D]): IorT[F, A, D] = iort.flatMap(f)

  override def tailRecM[B, D](b: B)(f: B => IorT[F, A, Either[B, D]]): IorT[F, A, D] =
    IorT(F0.tailRecM(Tuple2[B, Option[A]](b, None)) {
      case (b0, optionA) =>
        F0.map(f(b0).value) {
          case Ior.Left(aa)           => Right(Ior.Left(Semigroup.maybeCombine(optionA, aa)))
          case Ior.Right(Left(b1))    => Left(b1 -> optionA)
          case Ior.Right(Right(d))    => Right(optionA.fold(Ior.right[A, D](d))(Ior.both(_, d)))
          case Ior.Both(aa, Right(d)) => Right(Ior.both(Semigroup.maybeCombine(optionA, aa), d))
          case Ior.Both(aa, Left(b1)) => Left(b1 -> Some(Semigroup.maybeCombine(optionA, aa)))
        }
    })
}

sealed private[data] trait IorTMonadError[F[_], A] extends MonadError[IorT[F, A, *], A] with IorTMonad[F, A] {
  override def raiseError[B](a: A): IorT[F, A, B] = IorT(F0.pure(Ior.left(a)))

  override def handleErrorWith[B](iort: IorT[F, A, B])(f: A => IorT[F, A, B]): IorT[F, A, B] =
    IorT(F0.flatMap(iort.value) {
      case Ior.Left(a)                         => f(a).value
      case r @ (Ior.Right(_) | Ior.Both(_, _)) => F0.pure(r)
    })
}

sealed private[data] trait IorTMonadErrorF[F[_], A, E] extends MonadError[IorT[F, A, *], E] with IorTMonad[F, A] {
  implicit override def F0: MonadError[F, E]

  override def raiseError[B](e: E): IorT[F, A, B] = IorT(F0.raiseError(e))

  override def handleErrorWith[B](iort: IorT[F, A, B])(f: E => IorT[F, A, B]): IorT[F, A, B] =
    IorT(F0.handleErrorWith(iort.value)(f(_).value))
}

sealed private[data] trait IorTSemigroup[F[_], A, B] extends Semigroup[IorT[F, A, B]] {
  implicit def F0: Semigroup[F[Ior[A, B]]]

  override def combine(x: IorT[F, A, B], y: IorT[F, A, B]): IorT[F, A, B] =
    IorT(F0.combine(x.value, y.value))
}

sealed private[data] trait IorTMonoid[F[_], A, B] extends Monoid[IorT[F, A, B]] with IorTSemigroup[F, A, B] {
  implicit override def F0: Monoid[F[Ior[A, B]]]

  override def empty: IorT[F, A, B] = IorT(F0.empty)
}

sealed private[data] trait IorTFoldable[F[_], A] extends Foldable[IorT[F, A, *]] {
  implicit def F0: Foldable[F]

  override def foldLeft[B, C](iort: IorT[F, A, B], c: C)(f: (C, B) => C): C = iort.foldLeft(c)(f)

  override def foldRight[B, C](iort: IorT[F, A, B], lc: Eval[C])(f: (B, Eval[C]) => Eval[C]): Eval[C] =
    iort.foldRight(lc)(f)
}

sealed private[data] trait IorTTraverse[F[_], A] extends Traverse[IorT[F, A, *]] with IorTFoldable[F, A] {
  implicit override def F0: Traverse[F]

  override def traverse[G[_]: Applicative, B, D](iort: IorT[F, A, B])(f: B => G[D]): G[IorT[F, A, D]] = iort.traverse(f)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy