All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalaz.BindRec.scala Maven / Gradle / Ivy

The newest version!
package scalaz

////
/**
 * [[scalaz.Bind]] capable of using constant stack space when doing recursive
 * binds.
 *
 * Implementations of `tailrecM` should not make recursive calls without the
 * `@tailrec` annotation.
 *
  * Based on Phil Freeman's work on stack safety in PureScript, described in
  * [[http://functorial.com/stack-safety-for-free/index.pdf Stack Safety for
  * Free]].
 */
////
trait BindRec[F[_]] extends Bind[F] { self =>
  ////

  def tailrecM[A, B](a: A)(f: A => F[A \/ B]): F[B]

  override def forever[A, B](fa: F[A]): F[B] =
    tailrecM[Unit, B](())(u => map(fa)(_ => -\/(u)))

  /**The product of BindRec `F` and `G`, `[x](F[x], G[x]])`, is a BindRec */
  def product[G[_]](implicit G0: BindRec[G]): BindRec[λ[α => (F[α], G[α])]] =
    new ProductBindRec[F, G] {
      def F = self
      def G = G0
    }

  trait BindRecLaw extends BindLaw {
    def tailrecBindConsistency[A](a: A, f: A => F[A])(implicit FA: Equal[F[A]]): Boolean = {
      val g: ((Int, A)) => F[(Int, A) \/ A] = { case (i, a) =>
        if (i > 0)
          map(f(a))(a => -\/((i-1, a)))
        else
          map(f(a))(\/.right)
      }

      val result = tailrecM[(Int, A), A]((4, a))(g)
      val expected = bind(f(a))(a => bind(f(a))(a => bind(f(a))(a => bind(f(a))(f))))

      FA.equal(result, expected)
    }
  }
  def bindRecLaw: BindRecLaw = new BindRecLaw {}

  ////
  val bindRecSyntax: scalaz.syntax.BindRecSyntax[F] =
    new scalaz.syntax.BindRecSyntax[F] { def F = BindRec.this }
}

object BindRec {
  @inline def apply[F[_]](implicit F: BindRec[F]): BindRec[F] = F

  import Isomorphism._

  def fromIso[F[_], G[_]](D: F <~> G)(implicit E: BindRec[G]): BindRec[F] =
    new IsomorphismBindRec[F, G] {
      override def G: BindRec[G] = E
      override def iso: F <~> G = D
    }

  ////
  implicit def idInstance: BindRec[Id.Id] = Id.id

  ////
}

trait IsomorphismBindRec[F[_], G[_]] extends BindRec[F] with IsomorphismBind[F, G]{
  implicit def G: BindRec[G]
  ////

  override def tailrecM[A, B](a: A)(f: A => F[A \/ B]): F[B] =
    iso.from(G.tailrecM(a)(f andThen iso.unlift[A \/ B].to))
  ////
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy