diff --git a/docs/src/main/tut/freemonad.md b/docs/src/main/tut/freemonad.md index 3fc33fdd27..b9a93c8b8c 100644 --- a/docs/src/main/tut/freemonad.md +++ b/docs/src/main/tut/freemonad.md @@ -477,16 +477,12 @@ If you look at implementation in cats, you will see another member of the `Free[_]` ADT: ```scala -sealed abstract case class Gosub[S[_], B]() extends Free[S, B] { - type C - val a: () => Free[S, C] - val f: C => Free[S, B] -} +case class FlatMapped[S[_], B, C](c: Free[S, C], f: C => Free[S, B]) extends Free[S, B] ``` -`Gosub` represents a call to a subroutine `a` and when `a` is +`FlatMapped` represents a call to a subroutine `c` and when `c` is finished, it continues the computation by calling the function `f` -with the result of `a`. +with the result of `c`. It is actually an optimization of `Free` structure allowing to solve a problem of quadratic complexity implied by very deep recursive `Free` @@ -494,7 +490,7 @@ computations. It is exactly the same problem as repeatedly appending to a `List[_]`. As the sequence of operations becomes longer, the slower a `flatMap` -"through" the structure will be. With `Gosub`, `Free` becomes a +"through" the structure will be. With `FlatMapped`, `Free` becomes a right-associated structure not subject to quadratic complexity. ## Future Work (TODO) diff --git a/free/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala index c7c6c5cf4c..c027c5b698 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -13,7 +13,7 @@ import cats.arrow.FunctionK */ sealed abstract class Free[S[_], A] extends Product with Serializable { - import Free.{ Pure, Suspend, Gosub } + import Free.{ Pure, Suspend, FlatMapped } final def map[B](f: A => B): Free[S, B] = flatMap(a => Pure(f(a))) @@ -23,7 +23,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { * All left-associated binds are reassociated to the right. */ final def flatMap[B](f: A => Free[S, B]): Free[S, B] = - Gosub(this, f) + FlatMapped(this, f) /** * Catamorphism. Run the first given function if Pure, otherwise, @@ -35,8 +35,8 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { /** Takes one evaluation step in the Free monad, re-associating left-nested binds in the process. */ @tailrec final def step: Free[S, A] = this match { - case Gosub(Gosub(c, f), g) => c.flatMap(cc => f(cc).flatMap(g)).step - case Gosub(Pure(a), f) => f(a).step + case FlatMapped(FlatMapped(c, f), g) => c.flatMap(cc => f(cc).flatMap(g)).step + case FlatMapped(Pure(a), f) => f(a).step case x => x } @@ -47,11 +47,11 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { final def resume(implicit S: Functor[S]): S[Free[S, A]] Xor A = this match { case Pure(a) => Right(a) case Suspend(t) => Left(S.map(t)(Pure(_))) - case Gosub(c, f) => + case FlatMapped(c, f) => c match { case Pure(a) => f(a).resume case Suspend(t) => Left(S.map(t)(f)) - case Gosub(d, g) => d.flatMap(dd => g(dd).flatMap(f)).resume + case FlatMapped(d, g) => d.flatMap(dd => g(dd).flatMap(f)).resume } } @@ -98,13 +98,13 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { S.pure(Xor.right(a)) case Suspend(ma) => S.map(ma)(Xor.right(_)) - case Gosub(curr, f) => + case FlatMapped(curr, f) => curr match { case Pure(x) => S.pure(Xor.left(f(x))) case Suspend(mx) => S.map(mx)(x => Xor.left(f(x))) - case Gosub(prev, g) => + case FlatMapped(prev, g) => S.pure(Xor.left(prev.flatMap(w => g(w).flatMap(f)))) } } @@ -123,7 +123,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { M.tailRecM(this)(_.step match { case Pure(a) => M.pure(Xor.right(a)) case Suspend(sa) => M.map(f(sa))(Xor.right) - case Gosub(c, g) => M.map(c.foldMap(f))(cc => Xor.left(g(cc))) + case FlatMapped(c, g) => M.map(c.foldMap(f))(cc => Xor.left(g(cc))) }) /** @@ -155,7 +155,7 @@ object Free { private[free] final case class Suspend[S[_], A](a: S[A]) extends Free[S, A] /** Call a subroutine and continue with the given function. */ - private[free] final case class Gosub[S[_], B, C](c: Free[S, C], f: C => Free[S, B]) extends Free[S, B] + private[free] final case class FlatMapped[S[_], B, C](c: Free[S, C], f: C => Free[S, B]) extends Free[S, B] /** * Lift a pure `A` value into the free monad. diff --git a/free/src/test/scala/cats/free/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala index b2db47c362..ef9b3e0a5d 100644 --- a/free/src/test/scala/cats/free/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -123,21 +123,21 @@ sealed trait FreeTestsInstances { } private def freeGen[F[_], A](maxDepth: Int)(implicit F: Arbitrary[F[A]], A: Arbitrary[A]): Gen[Free[F, A]] = { - val noGosub = Gen.oneOf( + val noFlatMapped = Gen.oneOf( A.arbitrary.map(Free.pure[F, A]), F.arbitrary.map(Free.liftF[F, A])) val nextDepth = Gen.chooseNum(1, maxDepth - 1) - def withGosub = for { + def withFlatMapped = for { fDepth <- nextDepth freeDepth <- nextDepth f <- arbFunction1[A, Free[F, A]](Arbitrary(freeGen[F, A](fDepth))).arbitrary freeFA <- freeGen[F, A](freeDepth) } yield freeFA.flatMap(f) - if (maxDepth <= 1) noGosub - else Gen.oneOf(noGosub, withGosub) + if (maxDepth <= 1) noFlatMapped + else Gen.oneOf(noFlatMapped, withFlatMapped) } implicit def freeArbitrary[F[_], A](implicit F: Arbitrary[F[A]], A: Arbitrary[A]): Arbitrary[Free[F, A]] =