Skip to content

Commit

Permalink
Remove unsafe Free methods, make Monad stack safety test JVM only
Browse files Browse the repository at this point in the history
  • Loading branch information
adelbertc committed Sep 13, 2016
1 parent 02c7e3e commit bb51f3c
Show file tree
Hide file tree
Showing 4 changed files with 8 additions and 25 deletions.
5 changes: 4 additions & 1 deletion core/src/main/scala/cats/Eval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,10 @@ private[cats] trait EvalInstances extends EvalInstances0 {
def flatMap[A, B](fa: Eval[A])(f: A => Eval[B]): Eval[B] = fa.flatMap(f)
def extract[A](la: Eval[A]): A = la.value
def coflatMap[A, B](fa: Eval[A])(f: Eval[A] => B): Eval[B] = Later(f(fa))
def tailRecM[A, B](a: A)(f: A => Eval[Either[A, B]]): Eval[B] = defaultTailRecM(a)(f)
def tailRecM[A, B](a: A)(f: A => Eval[Either[A, B]]): Eval[B] = f(a).flatMap { // OK because Eval is trampolined
case Left(nextA) => tailRecM(nextA)(f)
case Right(b) => pure(b)
}
}

implicit def catsOrderForEval[A: Order]: Order[Eval[A]] =
Expand Down
12 changes: 0 additions & 12 deletions core/src/main/scala/cats/Monad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,4 @@ import simulacrum.typeclass
@typeclass trait Monad[F[_]] extends FlatMap[F] with Applicative[F] {
override def map[A, B](fa: F[A])(f: A => B): F[B] =
flatMap(fa)(a => pure(f(a)))

/**
* This is not stack safe if the monad is not trampolined, but it
* is always lawful. It it better if you can find a stack safe way
* to write this method (all cats types have a stack safe version
* of this). When this method is safe you can find an `implicit r: RecursiveTailRecM`.
*/
protected def defaultTailRecM[A, B](a: A)(fn: A => F[Either[A, B]]): F[B] =
flatMap(fn(a)) {
case Right(b) => pure(b)
case Left(nextA) => defaultTailRecM(nextA)(fn)
}
}
10 changes: 1 addition & 9 deletions free/src/main/scala/cats/free/Free.scala
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,6 @@ sealed abstract class Free[S[_], A] extends Product with Serializable {
case FlatMapped(c, g) => M.map(c.foldMap(f))(cc => Left(g(cc)))
})

/**
* Same as foldMap but without a guarantee of stack safety. If the recursion is shallow
* enough, this will work
*/
final def foldMapUnsafe[M[_]](f: FunctionK[S, M])(implicit M: Monad[M]): M[A] =
foldMap[M](f)


/**
* Compile your free monad into another language by changing the
* suspension functor using the given natural transformation `f`.
Expand All @@ -143,7 +135,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable {
* effects will be applied by `compile`.
*/
final def compile[T[_]](f: FunctionK[S, T]): Free[T, A] =
foldMapUnsafe[Free[T, ?]] { // this is safe because Free is stack safe
foldMap[Free[T, ?]] { // this is safe because Free is stack safe
new FunctionK[S, Free[T, ?]] {
def apply[B](fa: S[B]): Free[T, B] = Suspend(f(fa))
}
Expand Down
6 changes: 3 additions & 3 deletions laws/src/main/scala/cats/laws/discipline/MonadTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cats
package laws
package discipline

import catalysts.Platform
import cats.laws.discipline.CartesianTests.Isomorphisms
import org.scalacheck.Arbitrary
import org.scalacheck.Prop
Expand Down Expand Up @@ -30,9 +31,8 @@ trait MonadTests[F[_]] extends ApplicativeTests[F] with FlatMapTests[F] {
def props: Seq[(String, Prop)] = Seq(
"monad left identity" -> forAll(laws.monadLeftIdentity[A, B] _),
"monad right identity" -> forAll(laws.monadRightIdentity[A] _),
"map flatMap coherence" -> forAll(laws.mapFlatMapCoherence[A, B] _),
"tailRecM stack safety" -> laws.tailRecMStackSafety
)
"map flatMap coherence" -> forAll(laws.mapFlatMapCoherence[A, B] _)
) ++ (if (Platform.isJvm) Seq[(String, Prop)]("tailRecM stack safety" -> laws.tailRecMStackSafety) else Seq.empty)
}
}
}
Expand Down

0 comments on commit bb51f3c

Please sign in to comment.