diff --git a/core/src/main/scala/cats/data/EitherT.scala b/core/src/main/scala/cats/data/EitherT.scala index a267f9684c..61e462ca40 100644 --- a/core/src/main/scala/cats/data/EitherT.scala +++ b/core/src/main/scala/cats/data/EitherT.scala @@ -49,7 +49,7 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) { /** * Inverse of `MonadError#attemptT` */ - def rethrowT(implicit F: MonadError[F, A]): F[B] = + def rethrowT(implicit F: MonadError[F, _ >: A]): F[B] = F.rethrow(value) def valueOr[BB >: B](f: A => BB)(implicit F: Functor[F]): F[BB] = fold(f, identity) diff --git a/core/src/main/scala/cats/syntax/either.scala b/core/src/main/scala/cats/syntax/either.scala index bca66e12db..c63cf5268c 100644 --- a/core/src/main/scala/cats/syntax/either.scala +++ b/core/src/main/scala/cats/syntax/either.scala @@ -276,6 +276,7 @@ final class EitherOps[A, B](private val eab: Either[A, B]) extends AnyVal { def toEitherNel[AA >: A]: EitherNel[AA, B] = leftMap(NonEmptyList.one) + @deprecated("use liftTo instead", "2.0.0") def raiseOrPure[F[_]](implicit ev: ApplicativeError[F, A]): F[B] = ev.fromEither(eab) diff --git a/core/src/main/scala/cats/syntax/validated.scala b/core/src/main/scala/cats/syntax/validated.scala index 6e76c52cf3..547011cf60 100644 --- a/core/src/main/scala/cats/syntax/validated.scala +++ b/core/src/main/scala/cats/syntax/validated.scala @@ -20,7 +20,7 @@ trait ValidatedExtensionSyntax { } final class ValidatedExtension[E, A](private val self: Validated[E, A]) extends AnyVal { - def liftTo[F[_]](implicit F: ApplicativeError[F, E]): F[A] = + def liftTo[F[_]](implicit F: ApplicativeError[F, _ >: E]): F[A] = new ApplicativeErrorExtensionOps(F).fromValidated(self) } diff --git a/tests/src/test/scala/cats/tests/EitherTSuite.scala b/tests/src/test/scala/cats/tests/EitherTSuite.scala index 0e637b2859..ce9886058b 100644 --- a/tests/src/test/scala/cats/tests/EitherTSuite.scala +++ b/tests/src/test/scala/cats/tests/EitherTSuite.scala @@ -285,6 +285,15 @@ class EitherTSuite extends CatsSuite { failed.attemptT.rethrowT should ===(failed) } + test("rethrowT works with specialized failures") { + implicit val eqThrow: Eq[Throwable] = Eq.fromUniversalEquals + val failed: Try[Int] = Failure(new IllegalArgumentException("error")) + + val t: EitherT[Try, IllegalArgumentException, Int] = + failed.attemptT.leftMap(_.asInstanceOf[IllegalArgumentException]) + t.rethrowT should ===(failed) + } + test("transform consistent with value.map") { forAll { (eithert: EitherT[List, String, Int], f: Either[String, Int] => Either[Long, Double]) => eithert.transform(f) should ===(EitherT(eithert.value.map(f))) diff --git a/tests/src/test/scala/cats/tests/ValidatedSuite.scala b/tests/src/test/scala/cats/tests/ValidatedSuite.scala index faf6fb70fe..a7bc31cb29 100644 --- a/tests/src/test/scala/cats/tests/ValidatedSuite.scala +++ b/tests/src/test/scala/cats/tests/ValidatedSuite.scala @@ -314,4 +314,13 @@ class ValidatedSuite extends CatsSuite { v.liftTo[Option] shouldBe v.toOption } } + + test("liftTo works with specialized errors") { + implicit val eqThrow: Eq[Throwable] = Eq.fromUniversalEquals + val ex: IllegalArgumentException = new IllegalArgumentException() + val validated: Validated[IllegalArgumentException, Int] = Validated.Invalid(ex) + val lifted: Either[Throwable, Int] = validated.liftTo[Either[Throwable, *]] + + lifted should ===(Left[Throwable, Int](ex)) + } }