diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index aa3de6150b..88bcab2cc0 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -93,6 +93,25 @@ final case class OptionT[F[_], A](value: F[Option[A]]) { object OptionT extends OptionTInstances { def pure[F[_], A](a: A)(implicit F: Applicative[F]): OptionT[F, A] = OptionT(F.pure(Some(a))) + + /** + * Transforms an `Option` into an `OptionT`, lifted into the specified `Applicative`. + * + * Note: The return type is a FromOptionAux[F], which has an apply method on it, allowing + * you to call fromOption like this: + * {{{ + * val t: Option[Int] = ... + * val x: OptionT[List, Int] = fromOption[List](t) + * }}} + * + * The reason for the indirection is to emulate currying type parameters. + */ + def fromOption[F[_]]: FromOptionAux[F] = new FromOptionAux + + class FromOptionAux[F[_]] private[OptionT] { + def apply[A](value: Option[A])(implicit F: Applicative[F]): OptionT[F, A] = + OptionT(F.pure(value)) + } } // TODO create prioritized hierarchy for Functor, Monad, etc diff --git a/tests/shared/src/test/scala/cats/tests/OptionTTests.scala b/tests/shared/src/test/scala/cats/tests/OptionTTests.scala index af5f130d47..9869d99b54 100644 --- a/tests/shared/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/shared/src/test/scala/cats/tests/OptionTTests.scala @@ -105,6 +105,12 @@ class OptionTTests extends CatsSuite { } }) + test("fromOption")(check { + forAll { (o: Option[Int]) => + List(o) == OptionT.fromOption[List](o).value + } + }) + checkAll("OptionT[List, Int]", MonadTests[OptionT[List, ?]].monad[Int, Int, Int]) checkAll("MonadOptionT[List, ?]]", SerializableTests.serializable(Monad[OptionT[List, ?]])) }