diff --git a/docs/src/main/tut/freemonad.md b/docs/src/main/tut/freemonad.md index 02d4e83f46..ad514d1bdb 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 5193642f63..4aae3ce21a 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -16,7 +16,7 @@ object Free { private final case class Suspend[S[_], A](a: S[A]) extends Free[S, A] /** Call a subroutine and continue with the given function. */ - private final case class Gosub[S[_], B, C](c: Free[S, C], f: C => Free[S, B]) extends Free[S, B] + private final case class FlatMapped[S[_], B, C](c: Free[S, C], f: C => Free[S, B]) extends Free[S, B] /** * Suspend a value within a functor lifting it to a Free. @@ -70,7 +70,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, @@ -82,8 +82,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 } @@ -94,11 +94,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 } } @@ -139,7 +139,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))) }) /** diff --git a/free/src/test/scala/cats/free/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala index 1d80a853fa..e4ac8b0e11 100644 --- a/free/src/test/scala/cats/free/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -96,21 +96,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]] =