From 885d33757318e6cb517ec44f822c80a2d83d172d Mon Sep 17 00:00:00 2001 From: Alessandro Lacava Date: Tue, 17 Nov 2015 10:27:58 +0100 Subject: [PATCH 1/3] add OptionT.liftF and update related docs and tests. Close issue #659 --- core/src/main/scala/cats/data/OptionT.scala | 6 +++++ docs/src/main/tut/optiont.md | 27 +++++++++++++++++++ .../test/scala/cats/tests/OptionTTests.scala | 6 +++++ 3 files changed, 39 insertions(+) diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 3fbd5ae338..54ec768b7f 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -120,6 +120,12 @@ object OptionT extends OptionTInstances { def apply[A](value: Option[A])(implicit F: Applicative[F]): OptionT[F, A] = OptionT(F.pure(value)) } + + /** + * Lifts the `F[A]` Functor into an `OptionT[F, A]`. + * + */ + def liftF[F[_], A](fa: F[A])(implicit F: Functor[F]): OptionT[F, A] = OptionT(F.map(fa)(Option(_))) } private[data] sealed trait OptionTInstances1 { diff --git a/docs/src/main/tut/optiont.md b/docs/src/main/tut/optiont.md index 6b868731d1..3ae5162248 100644 --- a/docs/src/main/tut/optiont.md +++ b/docs/src/main/tut/optiont.md @@ -51,6 +51,33 @@ val noWelcome: OptionT[Future, String] = customGreetingT.filterNot(_.contains("w val withFallback: Future[String] = customGreetingT.getOrElse("hello, there!") ``` +## From `Option[A]` and/or `F[A]` to `OptionT[F, A]` + +Sometimes you may have an `Option[A]` and/or `F[A]` and want to *lift* them into an `OptionT[F, A]`. For this purpose `OptionT` exposes two useful methods, namely `fromOption` and `liftF`, respectively. E.g.: + +```tut:silent +import scala.concurrent.Future +import scala.concurrent.ExecutionContext.Implicits.global + +import cats.data.OptionT +import cats.std.future._ + +val greetingFO: Future[Option[String]] = Future.successful(Some("Hello")) + +val firstnameF: Future[String] = Future.successful("Jane") + +val lastnameO: Option[String] = Some("Doe") + +val ot: OptionT[Future, String] = for { + g <- OptionT(greetingFO) + f <- OptionT.liftF(firstnameF) + l <- OptionT.fromOption(lastnameO) +} yield s"$g $f $l" + +val result: Future[Option[String]] = ot.value // Future(Some("Hello Jane Doe")) + +``` + ## Beyond map Sometimes the operation you want to perform on an `Option[Future[String]]` might not be as simple as just wrapping the `Option` method in a `Future.map` call. For example, what if we want to greet the customer with their custom greeting if it exists but otherwise fall back to a default `Future[String]` greeting? Without `OptionT`, this implementation might look like: diff --git a/tests/src/test/scala/cats/tests/OptionTTests.scala b/tests/src/test/scala/cats/tests/OptionTTests.scala index 2c21b509fd..ca7cd405d1 100644 --- a/tests/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTTests.scala @@ -111,6 +111,12 @@ class OptionTTests extends CatsSuite { } } + test("liftF") { + forAll { (xs: List[Int]) => + xs.map(Option(_)) should ===(OptionT.liftF(xs).value) + } + } + test("show"){ val xor: String Xor Option[Int] = Xor.right(Some(1)) OptionT[Xor[String, ?], Int](xor).show should === ("Xor.Right(Some(1))") From 02313fd995c823d5be6c2fe6a09eda5706e6116f Mon Sep 17 00:00:00 2001 From: Alessandro Lacava Date: Tue, 17 Nov 2015 10:33:58 +0100 Subject: [PATCH 2/3] update authors --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 7095a0edc6..9dbc1073ff 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -13,6 +13,7 @@ This file lists the people whose contributions have made Cats possible: * Adelbert Chang + * Alessandro Lacava * Alissa Pajer * Alistair Johnson * Amir Mohammad Saied From 4383dcc8577eec30066486a0d4c02d1251019c42 Mon Sep 17 00:00:00 2001 From: Alessandro Lacava Date: Tue, 17 Nov 2015 14:22:18 +0100 Subject: [PATCH 3/3] change Option(_) to Some(_) and remove already-imported imports from docs --- core/src/main/scala/cats/data/OptionT.scala | 2 +- docs/src/main/tut/optiont.md | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 54ec768b7f..913a6a5595 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -125,7 +125,7 @@ object OptionT extends OptionTInstances { * Lifts the `F[A]` Functor into an `OptionT[F, A]`. * */ - def liftF[F[_], A](fa: F[A])(implicit F: Functor[F]): OptionT[F, A] = OptionT(F.map(fa)(Option(_))) + def liftF[F[_], A](fa: F[A])(implicit F: Functor[F]): OptionT[F, A] = OptionT(F.map(fa)(Some(_))) } private[data] sealed trait OptionTInstances1 { diff --git a/docs/src/main/tut/optiont.md b/docs/src/main/tut/optiont.md index 3ae5162248..ef751b3ad3 100644 --- a/docs/src/main/tut/optiont.md +++ b/docs/src/main/tut/optiont.md @@ -56,12 +56,6 @@ val withFallback: Future[String] = customGreetingT.getOrElse("hello, there!") Sometimes you may have an `Option[A]` and/or `F[A]` and want to *lift* them into an `OptionT[F, A]`. For this purpose `OptionT` exposes two useful methods, namely `fromOption` and `liftF`, respectively. E.g.: ```tut:silent -import scala.concurrent.Future -import scala.concurrent.ExecutionContext.Implicits.global - -import cats.data.OptionT -import cats.std.future._ - val greetingFO: Future[Option[String]] = Future.successful(Some("Hello")) val firstnameF: Future[String] = Future.successful("Jane")