From 4f0f016960e86118f4c4728c661e653748b52071 Mon Sep 17 00:00:00 2001 From: valydia Date: Wed, 7 Mar 2018 18:38:36 +0000 Subject: [PATCH] Add Free.liftInject helper function (#1534) --- free/src/main/scala/cats/free/Free.scala | 20 +++++++++++++++++ free/src/test/scala/cats/free/FreeSuite.scala | 22 +++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/free/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala index b1d7ed956f..4e99bd8d9f 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -255,6 +255,7 @@ object Free extends FreeInstances { * This method exists to allow the `F` and `G` parameters to be * bound independently of the `A` parameter below. */ + // TODO to be deprecated / removed in cats 2.0 def inject[F[_], G[_]]: FreeInjectKPartiallyApplied[F, G] = new FreeInjectKPartiallyApplied @@ -266,6 +267,25 @@ object Free extends FreeInstances { Free.liftF(I.inj(fa)) } + /** + * This method is used to defer the application of an InjectK[F, G] + * instance. The actual work happens in + * `FreeLiftInjectKPartiallyApplied#apply`. + * + * This method exists to allow the `G` parameter to be + * bound independently of the `F` and `A` parameters below. + */ + def liftInject[G[_]]: FreeLiftInjectKPartiallyApplied[G] = + new FreeLiftInjectKPartiallyApplied + + /** + * Uses the [[http://typelevel.org/cats/guidelines.html#partially-applied-type-params Partially Applied Type Params technique]] for ergonomics. + */ + private[free] final class FreeLiftInjectKPartiallyApplied[G[_]](val dummy: Boolean = true ) extends AnyVal { + def apply[F[_], A](fa: F[A])(implicit I: InjectK[F, G]): Free[G, A] = + Free.liftF(I.inj(fa)) + } + def injectRoll[F[_], G[_], A](ga: G[Free[F, A]])(implicit I: InjectK[G, F]): Free[F, A] = Free.roll(I.inj(ga)) diff --git a/free/src/test/scala/cats/free/FreeSuite.scala b/free/src/test/scala/cats/free/FreeSuite.scala index 573df80fc9..6fe1218fb3 100644 --- a/free/src/test/scala/cats/free/FreeSuite.scala +++ b/free/src/test/scala/cats/free/FreeSuite.scala @@ -115,6 +115,8 @@ class FreeSuite extends CatsSuite { case class Test1[A](value : Int, f: Int => A) extends Test1Algebra[A] + def test1[A](value: Int, f: Int => A): Test1Algebra[A] = Test1(value, f) + object Test1Algebra { implicit def test1AlgebraAFunctor: Functor[Test1Algebra] = new Functor[Test1Algebra] { @@ -131,6 +133,8 @@ class FreeSuite extends CatsSuite { case class Test2[A](value : Int, f: Int => A) extends Test2Algebra[A] + def test2[A](value: Int, f: Int => A): Test2Algebra[A] = Test2(value, f) + object Test2Algebra { implicit def test2AlgebraAFunctor: Functor[Test2Algebra] = new Functor[Test2Algebra] { @@ -165,14 +169,28 @@ class FreeSuite extends CatsSuite { (implicit I0: Test1Algebra :<: F, I1: Test2Algebra :<: F): Free[F, Int] = { for { - a <- Free.inject[Test1Algebra, F](Test1(x, identity)) - b <- Free.inject[Test2Algebra, F](Test2(y, identity)) + a <- Free.inject[Test1Algebra, F](test1(x, identity)) + b <- Free.inject[Test2Algebra, F](test2(y, identity)) } yield a + b } (res[T] foldMap eitherKInterpreter) == (x + y) should ===(true) } } + test(".liftInject") { + forAll { (x: Int, y: Int) => + def res[F[_]] + (implicit I0: Test1Algebra :<: F, + I1: Test2Algebra :<: F): Free[F, Int] = { + for { + a <- Free.liftInject[F](test1(x, identity)) + b <- Free.liftInject[F](test2(y, identity)) + } yield a + b + } + (res[T] foldMap eitherKInterpreter) == (x + y) should ===(true) + } + } + val x: Free[T, Int] = Free.inject[Test1Algebra, T](Test1(1, identity)) test(".injectRoll") {