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

added liftTo to Try, Either and Option #2179

Merged
merged 1 commit into from
Mar 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this works because LiftToPartiallyApplied is marked private[cats] … I guess I can have a value of this type but I can't declare or ascribe it. Interesting trick.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the fun of working with scala.


}

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)
}
}