Skip to content

Commit

Permalink
Rename Gosub to FlatMapped
Browse files Browse the repository at this point in the history
  • Loading branch information
adelbertc committed Jun 10, 2016
1 parent 5cf9da3 commit 40c5fab
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 19 deletions.
12 changes: 4 additions & 8 deletions docs/src/main/tut/freemonad.md
Original file line number Diff line number Diff line change
Expand Up @@ -477,24 +477,20 @@ 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`
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)
Expand Down
14 changes: 7 additions & 7 deletions free/src/main/scala/cats/free/Free.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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
}

Expand All @@ -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
}
}

Expand Down Expand Up @@ -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)))
})

/**
Expand Down
8 changes: 4 additions & 4 deletions free/src/test/scala/cats/free/FreeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]] =
Expand Down

0 comments on commit 40c5fab

Please sign in to comment.