Skip to content

Commit

Permalink
added liftTo to Try Either and Option (#2179)
Browse files Browse the repository at this point in the history
  • Loading branch information
kailuowang authored Mar 2, 2018
1 parent 60c3546 commit 9135035
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 0 deletions.
26 changes: 26 additions & 0 deletions core/src/main/scala/cats/ApplicativeError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,30 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {

object ApplicativeError {
def apply[F[_], E](implicit F: ApplicativeError[F, E]): ApplicativeError[F, E] = F

private[cats] final class LiftFromOptionPartially[F[_]](val dummy: Boolean = true) extends AnyVal {
def apply[E, A](oa: Option[A], ifEmpty: => E)(implicit F: ApplicativeError[F, E]): F[A] =
oa match {
case Some(a) => F.pure(a)
case None => F.raiseError(ifEmpty)
}
}


/**
* lift from scala.Option[A] to a F[A]
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.ApplicativeError
*
* scala> ApplicativeError.liftFromOption[Either[String, ?]](Some(1), "Empty")
* res0: scala.Either[String, Int] = Right(1)
*
* scala> ApplicativeError.liftFromOption[Either[String, ?]](Option.empty[Int], "Empty")
* res1: scala.Either[String, Int] = Left(Empty)
* }}}
*/
def liftFromOption[F[_]]: LiftFromOptionPartially[F] = new LiftFromOptionPartially[F]
}
32 changes: 32 additions & 0 deletions core/src/main/scala/cats/syntax/TrySyntax.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cats
package syntax

import scala.util.Try

trait TrySyntax {
implicit final def catsSyntaxTry[A](t: Try[A]): TryOps[A] = new TryOps(t)
}


final class TryOps[A](val self: Try[A]) extends AnyVal {

/**
* lift the `try` into a `F[_]` with `ApplicativeError[F, Throwable]` instance
*
* {{{
* scala> import cats.implicits._
* scala> import util.Try
*
* scala> val s: Try[Int] = Try(3)
* scala> s.liftTo[Either[Throwable, ?]]
* res0: Either[Throwable, Int] = Right(3)
*
* scala> val f: Try[Int] = Try(throw new Throwable("boo"))
* scala> f.liftTo[Either[Throwable, ?]]
* res0: Either[Throwable, Int] = Left(java.lang.Throwable: boo)
* }}}
*/
def liftTo[F[_]](implicit F: ApplicativeError[F, Throwable]): F[A] =
F.fromTry(self)

}
2 changes: 2 additions & 0 deletions core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,5 @@ trait AllSyntax

trait AllSyntaxBinCompat0
extends UnorderedTraverseSyntax
with ApplicativeErrorExtension
with TrySyntax
33 changes: 33 additions & 0 deletions core/src/main/scala/cats/syntax/applicativeError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,39 @@ trait ApplicativeErrorSyntax {
new ApplicativeErrorOps[F, E, A](fa)
}

/**
* Extension to ApplicativeError in a binary compat way
*/
trait ApplicativeErrorExtension {
implicit final def catsSyntaxApplicativeErrorExtension[F[_], E](F: ApplicativeError[F, E]):
ApplicativeErrorExtensionOps[F, E] =
new ApplicativeErrorExtensionOps(F)
}

final class ApplicativeErrorExtensionOps[F[_], E](F: ApplicativeError[F, E]) {


/**
* Convert from scala.Option
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.ApplicativeError
* scala> val F = ApplicativeError[Either[String, ?], String]
*
* scala> F.fromOption(Some(1), "Empty")
* res0: scala.Either[String, Int] = Right(1)
*
* scala> F.fromOption(Option.empty[Int], "Empty")
* res1: scala.Either[String, Int] = Left(Empty)
* }}}
*/
def fromOption[A](oa: Option[A], ifEmpty: => E): F[A] =
ApplicativeError.liftFromOption(oa, ifEmpty)(F)

}

final class ApplicativeErrorIdOps[E](val e: E) extends AnyVal {
def raiseError[F[_], A](implicit F: ApplicativeError[F, E]): F[A] =
F.raiseError(e)
Expand Down
13 changes: 13 additions & 0 deletions core/src/main/scala/cats/syntax/either.scala
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,21 @@ final class EitherOps[A, B](val eab: Either[A, B]) extends AnyVal {
def raiseOrPure[F[_]](implicit ev: ApplicativeError[F, A]): F[B] =
ev.fromEither(eab)

/**
* lift the `Either` into a `F[_]` with `ApplicativeError[F, A]` instance
*
* {{{
* scala> import cats.implicits._
* scala> import cats.data.EitherT
* scala> val e: Either[String, Int] = Right(3)
* scala> e.liftTo[EitherT[Option, String, ?]]
* res0: cats.data.EitherT[Option, String, Int] = EitherT(Some(Right(3)))
* }}}
*/
def liftTo[F[_]](implicit F: ApplicativeError[F, A]): F[B] = F.fromEither(eab)
}


final class EitherObjectOps(val either: Either.type) extends AnyVal { // scalastyle:off ensure.single.space.after.token
def left[A, B](a: A): Either[A, B] = Left(a)

Expand Down
25 changes: 25 additions & 0 deletions core/src/main/scala/cats/syntax/option.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cats
package syntax

import cats.data.{Ior, Validated, ValidatedNel}
import cats.syntax.OptionOps.LiftToPartiallyApplied

trait OptionSyntax {
final def none[A]: Option[A] = Option.empty[A]
Expand Down Expand Up @@ -170,4 +171,28 @@ final class OptionOps[A](val oa: Option[A]) extends AnyVal {
* }}}
*/
def orEmpty(implicit A: Monoid[A]): A = oa.getOrElse(A.empty)

/**
* Lift to a F[A] as long as it has an ApplicativeError[F] instance
*
* Example:
* {{{
* scala> import cats.implicits._
*
* scala> Some(1).liftTo[Either[String, ?]]("Empty")
* res0: scala.Either[String, Int] = Right(1)
*
* scala> Option.empty[Int].liftTo[Either[String, ?]]("Empty")
* res1: scala.Either[String, Int] = Left(Empty)
* }}}
*/
def liftTo[F[_]]: LiftToPartiallyApplied[F, A] = new LiftToPartiallyApplied(oa)

}

object OptionOps {
private[syntax] final class LiftToPartiallyApplied[F[_], A](oa: Option[A]) {
def apply[E](ifEmpty: => E)(implicit F: ApplicativeError[F, E]): F[A] =
ApplicativeError.liftFromOption(oa, ifEmpty)
}
}

0 comments on commit 9135035

Please sign in to comment.