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

org.specs2.control.eff.Interpret.scala Maven / Gradle / Ivy

The newest version!
package org.specs2.control.eff

import scalaz._, Scalaz._
import Eff._

/**
 * Support methods to create interpreters (or "effect handlers") for a given effect M and a value Eff[R, A]
 * when M is a member of R.
 *
 * Those methods guarantee a stack-safe behaviour when running on a large list of effects
 * (in list.traverse(f) for example).
 *
 * There are different types of supported interpreters:
 *
 *  1. "interpret" + Recurse
 *
 *  This interpreter is used to handle effects which either return a value X from M[X] or stops with Eff[R, B]
 *  See an example of such an interpreter in Eval where we just evaluate a computation X for each Eval[X].
 *
 *  2. "interpretState" + StateRecurse
 *
 *  This interpreter is used to handle effects which either return a value X from M[X] or stops with Eff[R, B]
 *
 *  3. "interpretLoop" + Loop
 *
 *  The most generic kind of interpreter where we can even recurse in the case of Pure(a) (See ListEffect for such a use)
 *
 *  4. "intercept / interceptState / interceptLoop" methods are similar but they transform an effect to other effects in
 *  the same stack without removing it from the stack
 *
 *  5. "transform" to swap an effect T of a stack to another effect, using a Natural Transformation
 *
 *  6. "translate" to interpret one effect of a stack into other effects of the same stack using a Natural Transformation
 *     this is a specialized version of interpret + Recurse
 *
 *  7. "interpretUnsafe + SideEffect" when you have a side effecting function M[X] => X
 */
trait Interpret {

  /**
   * interpret the effect M in the R stack
   */
  def interpret[R, U, M[_], A, B](pure: A => Eff[U, B], recurse: Recurse[M, U, B])(effects: Eff[R, A])(implicit m: Member.Aux[M, R, U]): Eff[U, B] = {
    val loop = new Loop[M, R, A, Eff[U, B], Eff[U, Unit]] {
      type S = Unit
      val init = ()

      def onPure(a: A, s: Unit): (Eff[R, A], Unit) Either Eff[U, B] =
        Right(pure(a))

      def onEffect[X](mx: M[X], continuation: Arrs[R, X, A], s: Unit): (Eff[R, A], Unit) Either Eff[U, B] =
        recurse(mx).bimap(x => (continuation(x), ()), identity)

      def onLastEffect[X](mx: M[X], continuation: Arrs[R, X, Unit], s: Unit): (Eff[R, Unit], Unit) Either Eff[U, Unit] =
        recurse(mx).map(_.void).bimap(x => (continuation(x), ()), identity)

      def onApplicativeEffect[X, T[_] : Traverse](mx: T[M[X]], continuation: Arrs[R, T[X], A], s: Unit): (Eff[R, A], Unit) Either Eff[U, B] =
        recurse.applicative(mx) match {
          case Left(xs) => Left((continuation(xs), s))
          case Right(mlx) => onEffect(mlx, continuation, s)
        }

      def onLastApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], Unit], s: S): (Eff[R, Unit], S) Either Eff[U, Unit] =
        recurse.applicative(xs) match {
          case Left(xs1) => Left((continuation(xs1), s))
          case Right(mlx) => onLastEffect(mlx, continuation, s)
        }

    }
    interpretLoop[R, U, M, A, B](pure, loop)(effects)
  }

  /**
   * simpler version of interpret where the pure value is just mapped to another type
   */
  def interpret1[R, U, M[_], A, B](pure: A => B)(recurse: Recurse[M, U, B])(effects: Eff[R, A])(implicit m: Member.Aux[M, R, U]): Eff[U, B] =
  interpret[R, U, M, A, B]((a: A) => EffMonad[U].pure(pure(a)), recurse)(effects)

  /**
   * Helper trait for computations
   * which might produce several M[X] in a stack of effects and which need to keep some state around
   *
   * This is typically the case for Writer or State which need to keep some state S after each evaluation
   * Evaluating the effect M[X] might use the previous S value as shown in the `apply method`
   *
   * Finally when the Eff[R, A] returns an A, this one can be combined with the last state value to produce a B
   *
   */
  trait StateRecurse[M[_], A, B] {
    type S
    val init: S
    def apply[X](x: M[X], s: S): (X, S)
    def applicative[X, T[_] : Traverse](xs: T[M[X]], s: S): (T[X], S) Either (M[T[X]], S)
    def finalize(a: A, s: S): B
  }

  /**
   * interpret the effect M in the M |: R stack, keeping track of some state
   */
  def interpretState[R, U, M[_], A, B](pure: A => Eff[U, B], recurse: StateRecurse[M, A, B])(effects: Eff[R, A])(implicit m: Member.Aux[M, R, U]): Eff[U, B] = {
    val loop = new Loop[M, R, A, Eff[U, B], Eff[U, Unit]] {
      type S = recurse.S
      val init: S = recurse.init

      def onPure(a: A, s: S): (Eff[R, A], S) Either Eff[U, B] =
        Right(EffMonad[U].pure(recurse.finalize(a, s)))

      def onEffect[X](mx: M[X], continuation: Arrs[R, X, A], s: S): (Eff[R, A], S) Either Eff[U, B] =
        Left { recurse(mx, s) match { case (a, b) => (continuation(a), b)} }

      def onLastEffect[X](mx: M[X], continuation: Arrs[R, X, Unit], s: S): (Eff[R, Unit], S) Either Eff[U, Unit] =
        Left { recurse(mx, s) match { case (a, b) => (continuation(a), b)} }

      def onApplicativeEffect[X, T[_] : Traverse](mx: T[M[X]], continuation: Arrs[R, T[X], A], s: S): (Eff[R, A], S) Either Eff[U, B] =
        recurse.applicative(mx, s) match {
          case Left((ls, s1))   => Left((continuation(ls), s1))
          case Right((mlx, s1)) => onEffect(mlx, continuation, s1)
        }

      def onLastApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], Unit], s: S): (Eff[R, Unit], S) Either Eff[U, Unit] =
        recurse.applicative(xs, s) match {
          case Left((ls, s1))   => Left((continuation(ls), s1))
          case Right((mlx, s1)) => onLastEffect(mlx, continuation, s1)
        }

    }
    interpretLoop(pure, loop)(effects)
  }

  /**
   * simpler version of interpret1 where the pure value is just mapped to another type
   */
  def interpretState1[R, U, M[_], A, B](pure: A => B)(recurse: StateRecurse[M, A, B])(effects: Eff[R, A])(implicit m: Member.Aux[M, R, U]): Eff[U, B] =
  interpretState((a: A) => EffMonad[U].pure(pure(a)), recurse)(effects)

  /**
   * generalization of interpret and interpretState
   *
   * This method contains a loop which is stack-safe
   */
  def interpretLoop[R, U, M[_], A, B](pure: A => Eff[U, B], loop: Loop[M, R, A, Eff[U, B], Eff[U, Unit]])(effects: Eff[R, A])(implicit m: Member.Aux[M, R, U]): Eff[U, B] = {
    def goLast(ls: Last[R], s: loop.S): Eff[U, Unit] =
      ls match {
        case Last(None) => Eff.pure[U, Unit](())
        case Last(Some(l)) =>
          l.value match {
            case Pure(u, last) => goLast(last, s)

            case Impure(union, continuation, last) =>
              m.project(union) match {
                case Right(mx) =>
                  loop.onLastEffect(mx, continuation, s) match {
                    case Left((x, s1)) => goLast(Last.eff(x.addLast(last)), s1)
                    case Right(b)      => goLast(last, s)
                  }
                case Left(u) =>
                  Impure[U, union.X, Unit](u, Arrs.singleton(x => goLast(Last.eff(continuation(x).addLast(last)), s)))
              }

            case ap @ ImpureAp(unions, continuation, last) =>
              val collected = unions.project

              if (collected.effects.isEmpty)
                collected.othersEff(Arrs.singleton(x => goLast(Last.eff(continuation(x).addLast(last)), s)))
              else
                loop.onLastApplicativeEffect(collected.effects, collected.continuation(continuation, m), s) match {
                  case Left((x, s1)) => goLast(Last.eff(x.addLast(last)), s1)
                  case Right(b)      => goLast(last, s)
                }
          }
      }

    def go(eff: Eff[R, A], s: loop.S): Eff[U, B] = {
      eff match {
        case Pure(a, last) =>
          loop.onPure(a, s) match {
            case Left((a1, s1)) => go(a1.addLast(last), s1)
            case Right(b)       => b.addLast(goLast(last, s))
          }

        case Impure(union, continuation, last) =>
          m.project(union) match {
            case Right(v) =>
              loop.onEffect(v, continuation, s) match {
                case Left((x, s1)) => go(x.addLast(last), s1)
                case Right(b)      => b.addLast(goLast(last, s))
              }

            case Left(u) =>
              Impure[U, union.X, B](u, Arrs.singleton(x => go(continuation(x), s)), Last.eff(goLast(last, s)))
          }

        case ap @ ImpureAp(unions, continuation, last) =>
          val collected = unions.project

          if (collected.effects.isEmpty)
            collected.othersEff(Arrs.singleton(x => go(continuation(x).addLast(last), s)))
          else
            loop.onApplicativeEffect(collected.effects, collected.continuation(continuation, m), s) match {
              case Left((x, s1)) => go(x.addLast(last), s1)
              case Right(b)      => b.addLast(goLast(last, s))
            }
      }
    }

    go(effects, loop.init)
  }

  def interpretLoop1[R, U, M[_], A, B](pure: A => B)(loop: Loop[M, R, A, Eff[U, B], Eff[U, Unit]])(effects: Eff[R, A])(implicit m: Member.Aux[M, R, U]): Eff[U, B] =
    interpretLoop[R, U, M, A, B]((a: A) => EffMonad[U].pure(pure(a)), loop)(effects)

  /**
   * generalization of interpret
   *
   * This method contains a loop which is stack-safe
   */
  def interpretStatelessLoop[R, U, M[_], A, B](pure: A => Eff[U, B], loop: StatelessLoop[M, R, A, Eff[U, B], Eff[U, Unit]])(effects: Eff[R, A])(implicit m: Member.Aux[M, R, U]): Eff[U, B] =
  interpretLoop[R, U, M, A, B](pure, new Loop[M, R, A, Eff[U, B], Eff[U, Unit]] {
    type S = Unit
    val init: S = ()
    def onPure(a: A, s: S) = loop.onPure(a).leftMap((_, init))
    def onEffect[X](x: M[X], continuation: Arrs[R, X, A], s: S) = loop.onEffect(x, continuation).leftMap((_, init))
    def onLastEffect[X](x: M[X], continuation: Arrs[R, X, Unit], s: S) = loop.onLastEffect(x, continuation).leftMap((_, init))
    def onApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], A], s: S) = loop.onApplicativeEffect(xs, continuation).leftMap((_, init))
    def onLastApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], Unit], s: S) = loop.onLastApplicativeEffect(xs, continuation).leftMap((_, init))
  })(effects)(m)

  def interpretStatelessLoop1[R, U, M[_], A, B](pure: A => B)(loop: StatelessLoop[M, R, A, Eff[U, B], Eff[U, Unit]])(effects: Eff[R, A])(implicit m: Member.Aux[M, R, U]): Eff[U, B] =
    interpretStatelessLoop[R, U, M, A, B]((a: A) => EffMonad[U].pure(pure(a)), loop)(effects)

  /**
   * INTERPRET IN THE SAME STACK
   */
  def intercept[R, M[_], A, B](pure: A => Eff[R, B], recurse: Recurse[M, R, B])(effects: Eff[R, A])(implicit m: M /= R): Eff[R, B] = {
    val loop = new Loop[M, R, A, Eff[R, B], Eff[R, Unit]] {
      type S = Unit
      val init = ()

      def onPure(a: A, s: Unit): (Eff[R, A], Unit) Either Eff[R, B] =
        Right(pure(a))

      def onEffect[X](mx: M[X], continuation: Arrs[R, X, A], s: S): (Eff[R, A], Unit) Either Eff[R, B] =
        recurse(mx).bimap(x => (continuation(x), ()), identity)

      def onLastEffect[X](mx: M[X], continuation: Arrs[R, X, Unit], s: S): (Eff[R, Unit], S) Either Eff[R, Unit] =
        recurse(mx).map(_.void).bimap(x => (continuation(x), ()), identity)

      def onApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], A], s: S): (Eff[R, A], S) Either Eff[R, B] =
        recurse.applicative(xs) match {
          case Left(ls)   => Left((continuation(ls), s))
          case Right(mlx) => onEffect(mlx, continuation, s)
        }

      def onLastApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], Unit], s: S): (Eff[R, Unit], S) Either Eff[R, Unit] =
        recurse.applicative(xs) match {
          case Left(ls)   => Left((continuation(ls), s))
          case Right(mlx) => onLastEffect(mlx, continuation, s)
        }

    }
    interceptLoop[R, M, A, B](pure, loop)(effects)
  }

  /**
   * simpler version of intercept where the pure value is just mapped to another type
   */
  def intercept1[R, M[_], A, B](pure: A => B)(recurse: Recurse[M, R, B])(effects: Eff[R, A])(implicit m: M /= R): Eff[R, B] =
  intercept[R, M, A, B]((a: A) => EffMonad[R].pure(pure(a)), recurse)(effects)

  /**
   * intercept an effect and interpret it in the same stack.
   * This method is stack-safe
   */
  def interceptLoop[R, M[_], A, B](pure: A => Eff[R, B], loop: Loop[M, R, A, Eff[R, B], Eff[R, Unit]])(effects: Eff[R, A])(implicit m: M /= R): Eff[R, B] = {
    def goLast(ls: Last[R], s: loop.S): Eff[R, Unit] =
      ls match {
        case Last(None) => Eff.pure[R, Unit](())
        case Last(Some(l)) =>
          l.value match {
            case Pure(u, last) => goLast(last, s)

            case Impure(union, continuation, last) =>
              m.extract(union) match {
                case Some(mx) =>
                  loop.onLastEffect(mx, continuation, s) match {
                    case Left((x, s1)) => goLast(Last.eff(x.addLast(last)), s1)
                    case Right(b)      => goLast(last, s)
                  }
                case None =>
                  Impure[R, union.X, Unit](union, Arrs.singleton(x => goLast(Last.eff(continuation(x).addLast(last)), s)))
              }

            case ap @ ImpureAp(unions, continuation, last) =>
              val collected = unions.extract

              if (collected.effects.isEmpty)
                collected.othersEff(Arrs.singleton(x => goLast(Last.eff(continuation(x).addLast(last)), s)))
              else
                loop.onLastApplicativeEffect(collected.effects, collected.continuation(continuation), s) match {
                  case Left((x, s1)) => goLast(Last.eff(x.addLast(last)), s1)
                  case Right(b)      => goLast(last, s)
                }
          }
      }

    def go(eff: Eff[R, A], s: loop.S): Eff[R, B] = {
      eff match {
        case Pure(a, last) =>
          loop.onPure(a, s) match {
            case Left((a1, s1)) => go(a1, s1).addLast(goLast(last, s1))
            case Right(b) => b.addLast(goLast(last, s))
          }

        case Impure(union, continuation, last) =>
          m.extract(union) match {
            case Some(v) =>
              loop.onEffect(v, continuation, s) match {
                case Left((x, s1)) => go(x.addLast(last), s1)
                case Right(b)      => b.addLast(goLast(last, s))
              }

            case None =>
              Impure[R, union.X, B](union, Arrs.singleton(x => go(continuation(x).addLast(last), s)))
          }

        case ImpureAp(unions, continuation, last) =>
          val collect = unions.extract

          if (collect.effects.isEmpty)
            collect.othersEff(Arrs.singleton(x => go(continuation(x).addLast(last), s)))
          else
            loop.onApplicativeEffect(collect.effects, collect.continuation(continuation), s) match {
              case Left((x, s1)) => go(x.addLast(last), s1)
              case Right(b)      => b.addLast(goLast(last, s))
            }
      }
    }

    go(effects, loop.init)
  }

  def interceptLoop1[R, M[_], A, B](pure: A => B)(loop: Loop[M, R, A, Eff[R, B], Eff[R, Unit]])(effects: Eff[R, A])(implicit m: M /= R): Eff[R, B] =
    interceptLoop[R, M, A, B]((a: A) => EffMonad[R].pure(pure(a)), loop)(effects)

  def interceptStatelessLoop[R, M[_], A, B](pure: A => Eff[R, B], loop: StatelessLoop[M, R, A, Eff[R, B], Eff[R, Unit]])(effects: Eff[R, A])(implicit m: M /= R): Eff[R, B] =
    interceptLoop[R, M, A, B](pure, new Loop[M, R, A, Eff[R, B], Eff[R, Unit]] {
      type S = Unit
      val init: S = ()
      def onPure(a: A, s: S) = loop.onPure(a).leftMap((_, ()))
      def onEffect[X](x: M[X], continuation: Arrs[R, X, A], s: S) = loop.onEffect(x, continuation).leftMap((_, ()))
      def onLastEffect[X](x: M[X], continuation: Arrs[R, X, Unit], s: S) = loop.onLastEffect(x, continuation).leftMap((_, ()))
      def onApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], A], s: S) = loop.onApplicativeEffect(xs, continuation).leftMap((_, ()))
      def onLastApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], Unit], s: S) = loop.onLastApplicativeEffect(xs, continuation).leftMap((_, ()))
    })(effects)(m)

  def interceptStatelessLoop1[R, M[_], A, B](pure: A => B)(loop: StatelessLoop[M, R, A, Eff[R, B], Eff[R, Unit]])(effects: Eff[R, A])(implicit m: M /= R): Eff[R, B] =
    interceptStatelessLoop[R, M, A, B]((a: A) => EffMonad[R].pure(pure(a)), loop)(effects)

  /**
   * transform an effect into another one
   * using a natural transformation, leaving the rest of the stack untouched
   */
  def transform[SR, BR, U, TS[_], TB[_], A](r: Eff[SR, A], nat: TS ~> TB)
                                           (implicit sr: Member.Aux[TS, SR, U], br: Member.Aux[TB, BR, U]): Eff[BR, A] = {

    def goLast(ls: Last[SR]): Eff[BR, Unit] =
      ls match {
        case Last(None) => Eff.pure[BR, Unit](())
        case Last(Some(l)) =>
          l.value match {
            case Pure(u, last) => goLast(last)

            case Impure(union, continuation, last) =>
              sr.project(union) match {
                case Right(small) =>
                  Impure(br.inject(nat(small)), Arrs.singleton((x: union.X) => goLast(Last.eff(continuation(x).addLast(last)))))

                case Left(u1) =>
                  Impure(br.accept(u1), Arrs.singleton((x: union.X) => goLast(Last.eff(continuation(x).addLast(last)))))

              }

            case ap @ ImpureAp(unions, continuation, last) =>
              goLast(Last.eff(ap.toMonadic))
          }
      }

    def go(eff: Eff[SR, A]): Eff[BR, A] = {
      eff match {
        case Pure(a, last) => Pure[BR, A](a).addLast(goLast(last))

        case Impure(u, c, last) =>
          sr.project(u) match {
            case Right(small) =>
              Impure(br.inject(nat(small)), Arrs.singleton((x: u.X) => go(c(x).addLast(last))))

            case Left(u1) =>
              Impure(br.accept(u1), Arrs.singleton((x: u.X) => go(c(x).addLast(last))))
          }

        case ap @ ImpureAp(unions, continuation, last) =>
          ImpureAp(unions.transformInto(nat), Arrs.singleton(x => transform(continuation(x).addLast(last), nat)))
      }
    }

    go(r)
  }

  /**
   * Translate one effect of the stack into some of the other effects in the stack
   */
  def translate[R, U, T[_], A](effects: Eff[R, A])
                              (tr: Translate[T, U])
                              (implicit m: Member.Aux[T, R, U]): Eff[U, A] = {
    def goLast(ls: Last[R]): Eff[U, Unit] =
      ls match {
        case Last(None) => Eff.pure[U, Unit](())
        case Last(Some(l)) =>
          l.value match {
            case Pure(u, last) => goLast(last)

            case Impure(union, continuation, last) =>
              m.project(union) match {
                case Right(kv) =>
                  val effectsU: Eff[U, union.X] = tr(kv)
                  effectsU.flatMap(r => goLast(Last.eff(continuation(r).addLast(last))))

                case Left(u1) =>
                  Impure(u1, Arrs.singleton((x: union.X) => goLast(Last.eff(continuation(x).addLast(last)))))
              }

            case ap @ ImpureAp(unions, continuation, last) =>
              val collected = unions.project

              if (collected.effects.isEmpty)
                collected.othersEff(Arrs.singleton(x => goLast(Last.eff(continuation(x).addLast(last)))))
              else {
                val translated: Eff[U, List[Any]] = EffApplicative.traverse(collected.effects)(tr.apply)
                translated.flatMap(ls => goLast(Last.eff(collected.continuation(continuation, m).apply(ls).addLast(last))))
              }
          }
      }

    def go(eff: Eff[R, A]): Eff[U, A] = {
      eff match {
        case Pure(a, last) => Pure(a).addLast(goLast(last))

        case Impure(union, continuation, last) =>
          m.project(union) match {
            case Right(kv) =>
              val effectsU: Eff[U, union.X] = tr(kv)
              effectsU.flatMap(r => go(continuation(r).addLast(last)))

            case Left(u1) =>
              Impure(u1, Arrs.singleton((x: union.X) => go(continuation(x).addLast(last))))
          }

        case ap @ ImpureAp(unions, continuation, last) =>
          val collected = unions.project

          if (collected.effects.isEmpty)
            collected.othersEff(Arrs.singleton(x => go(continuation(x).addLast(last))))
          else {
            val translated: Eff[U, List[Any]] = EffApplicative.traverse(collected.effects)(tr.apply)
            translated.flatMap(ls => translate(collected.continuation(continuation, m).apply(ls).addLast(last))(tr))
          }
      }
    }

    go(effects)
  }

  /**
   * Translate one effect of the stack into some of the other effects in the stack
   * Using a natural transformation
   */
  def translateNat[R, U, T[_], A](effects: Eff[R, A])
                                 (nat: T ~> Eff[U, ?])
                                 (implicit m: Member.Aux[T, R, U]): Eff[U, A] =
  translate(effects)(new Translate[T, U] {
    def apply[X](tx: T[X]): Eff[U, X] = nat(tx)
  })

  /**
   * Translate one effect of the stack into other effects in a larger stack
   */
  def translateInto[R, T[_], U, A](eff: Eff[R, A])(translate: Translate[T, U])(implicit m: MemberInOut[T, R], into: IntoPoly[R, U]): Eff[U, A] =  {
    def goLast(ls: Last[R]): Eff[U, Unit] =
      ls match {
        case Last(None) => Eff.pure[U, Unit](())
        case Last(Some(l)) =>
          l.value match {
            case Pure(u, last) => goLast(last)

            case i @ Impure(union, continuation, last) =>
              m.extract(union) match {
                case Some(tx) => translate(tx).flatMap(x => goLast(Last.eff(continuation(x).addLast(last))))
                case None     => into(i).addLast(goLast(last))
              }

            case ap @ ImpureAp(unions, continuation, last) =>
              val translated: Eff[U, List[Any]] = Eff.traverseA(unions.extract.effects)(tx => translate(tx).addLast(goLast(last)))
              translated.flatMap(ts => goLast(Last.eff(continuation(ts).addLast(last))))
          }
      }

    eff match {
      case Pure(a, last) => into(eff).addLast(goLast(last))

      case Impure(u, c, last) =>
        m.extract(u) match {
          case Some(tx) => translate(tx).flatMap(x => translateInto(c(x).addLast(last))(translate))
          case None     => into(eff).addLast(goLast(last))
        }

      case ImpureAp(unions, c, last) =>
        val translated: Eff[U, List[Any]] = Eff.traverseA(unions.extract.effects)(tx => translate(tx).addLast(goLast(last)))
        translated.flatMap(ts => translateInto(c(ts).addLast(last))(translate))
    }
  }

  def augment[R, T[_], O[_], A](eff: Eff[R, A])(w: Augment[T, O])(implicit m: MemberInOut[T, R]): Eff[Fx.prepend[O, R], A] =  {
    type U = Fx.prepend[O, R]
    translateInto(eff)(new Translate[T, U] {
      def apply[X](tx: T[X]): Eff[U, X] = send[O, U, Unit](w(tx)) >> send[T, U, X](tx)
    })
  }

  /**
   * Intercept the values for one effect and transform them into
   * other values for the same effect
   */
  def interceptNat[R, T[_], A](effects: Eff[R, A])
                              (nat: T ~> T)
                              (implicit m: MemberInOut[T, R]): Eff[R, A] = {

    def goLast(ls: Last[R]): Eff[R, Unit] =
      ls match {
        case Last(None) => Eff.pure[R, Unit](())
        case Last(Some(l)) =>
          l.value match {
            case Pure(u, last) => goLast(last)

            case Impure(union, continuation, last) =>
              m.extract(union) match {
                case None     => Impure(union, Arrs.singleton((x: union.X) => goLast(Last.eff(continuation(x).addLast(last)))))
                case Some(tx) => Impure(m.inject(nat(tx)), Arrs.singleton((x: union.X) => goLast(Last.eff(continuation(x).addLast(last)))))
              }

            case ap @ ImpureAp(unions, continuation, last) =>
              ImpureAp(unions.transform(nat), Arrs.singleton(x => goLast(Last.eff(continuation(x).addLast(last)))))
          }
      }

    effects match {
      case Pure(a, last) => Pure(a).addLast(goLast(last))

      case Impure(union, continuation, last) =>
        m.extract(union) match {
          case None     => Impure(union, Arrs.singleton((x: union.X) => interceptNat(continuation(x).addLast(last))(nat)))
          case Some(tx) => Impure(m.inject(nat(tx)), Arrs.singleton((x: union.X) => interceptNat(continuation(x).addLast(last))(nat)))
        }

      case ImpureAp(unions, continuation, last) =>
        ImpureAp(unions.transform(nat), Arrs.singleton(x => interceptNat(continuation(x).addLast(last))(nat)))
    }
  }

  type of[F[_], G[_]] = {type l[A] = F[G[A]]}

  /**
   * Intercept the values for one effect,
   * emitting new values for the same effect inside a monad which is interleaved in
   */
  def interceptNatM[R, T[_], F[_], A](effects: Eff[R, A], nat: T ~> (T `of` F)#l)
                                     (implicit m: MemberInOut[T, R], FT: Traverse[F], FM: Monad[F]): Eff[R, F[A]] = {
    effects match {
      case Pure(a, last) =>
        pure[R, F[A]](FM.point(a)).addLast(last)

      case Impure(u, c, last) =>
        m.extract(u) match {
          case Some(tx) =>
            val union = m.inject(nat(tx))

            Impure(union, Arrs.singleton({ ex: F[u.X] =>
              Eff.flatTraverseA(ex)(x => interceptNatM[R, T, F, A](c(x), nat))
            }), last)

          case None => Impure(u, c.mapLast(r => interceptNatM[R, T, F, A](r, nat)), last)
        }

      case ImpureAp(unions, continuation, last) =>
        def materialize(u: Union[R, Any]): Union[R, Any] =
          m.extract(u) match {
            case Some(tx) => m.inject(nat(tx).asInstanceOf[T[Any]])
            case None => u
          }

        val materializedUnions =
          Unions(materialize(unions.first), unions.rest.map(materialize))

        val collected = unions.extract(m)
        val continuation1 = Arrs.singleton[R, List[Any], F[A]]({ ls: List[Any] =>
          val xors =
            ls.zipWithIndex.collect { case (a, i) =>
              if (collected.indices.contains(i)) a.asInstanceOf[F[Any]]
              else FM.pure(a)
            }.sequence

          Eff.flatTraverseA(xors)(x => interceptNatM[R, T, F, A](continuation(x), nat))

        })

        ImpureAp(materializedUnions, continuation1, last)
    }
  }

  /** interpret an effect by running side-effects */
  def interpretUnsafe[R, U, T[_], A](effects: Eff[R, A])
                                    (sideEffect: SideEffect[T])
                                    (implicit m: Member.Aux[T, R, U]): Eff[U, A] = {
    val recurse = new Recurse[T, m.Out, A] {
      def apply[X](tx: T[X]): X Either Eff[m.Out, A] =
        Left(sideEffect(tx))

      def applicative[X, Tr[_]: Traverse](ms: Tr[T[X]]): Tr[X] Either T[Tr[X]] =
        Left(ms.map(sideEffect.apply))
    }
    interpret1((a: A) => a)(recurse)(effects)(m)
  }
}

object Interpret extends Interpret

/**
 * Helper trait for computations
 * which might produce several M[X] in a stack of effects.
 *
 * Either we can produce an X to pass to a continuation or we're done
 *
 * For the applicative case we expect to be able to traverse a list
 * of effects and return an effect of a list of results OR
 * completely consume the effect and return a pure list of values
 */
trait Recurse[M[_], R, A] {
  def apply[X](m: M[X]): X Either Eff[R, A]
  def applicative[X, T[_]: Traverse](ms: T[M[X]]): T[X] Either M[T[X]]
}

/**
 * Generalisation of Recurse and StateRecurse
 *
 * The loop defines some state with an initial value which is maintained at
 * each step of the interpretation.
 *
 * A is the type of Eff values to interpret, and B is the result of the
 * interpretation (generally an other Eff value)
 *
 * C is the type of result for "last" actions.
 *
 * - the interpretation of a Pure value either returns the final result or possibly
 *   one more Eff value to interpret
 *
 * - onEffect interprets one effect and possibly uses the continuation to produce the next
 *   value to interpret. If no X can be used to run the continuation we might just
 *   output one final B value
 *
 *  - onLastEffect interprets the last effect of an Eff value. The only difference with onEffect
 *    is the fact that last actions return Unit values (and not A values)
 *
 *  - onApplicativeEff interprets a list of effects and possibly uses the continuation to
 *    get to the next value to interpret. If no interpretation can be done, a B value might be returned
 *
 *  - onLastApplicativeEffect does the same thing for last actions
 *
 */
trait Loop[M[_], R, A, B, C] {
  type S
  val init: S

  def onPure(a: A, s: S): (Eff[R, A], S) Either B

  def onEffect[X](x: M[X], continuation: Arrs[R, X, A], s: S): (Eff[R, A], S) Either B
  def onLastEffect[X](x: M[X], continuation: Arrs[R, X, Unit], s: S): (Eff[R, Unit], S) Either C

  def onApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], A], s: S): (Eff[R, A], S) Either B
  def onLastApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], Unit], s: S): (Eff[R, Unit], S) Either C
}

/**
 * Generalisation of Recurse
 */
trait StatelessLoop[M[_], R, A, B, C] {
  def onPure(a: A): Eff[R, A] Either B

  def onEffect[X](x: M[X], continuation: Arrs[R, X, A]): Eff[R, A] Either B
  def onLastEffect[X](x: M[X], continuation: Arrs[R, X, Unit]): Eff[R, Unit] Either C

  def onApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], A]): Eff[R, A] Either B
  def onLastApplicativeEffect[X, T[_] : Traverse](xs: T[M[X]], continuation: Arrs[R, T[X], Unit]): Eff[R, Unit] Either C
}

/**
 * trait for translating one effect into other ones in the same stack
 */
trait Translate[T[_], U] {
  def apply[X](kv: T[X]): Eff[U, X]
}

trait SideEffect[T[_]] {
  def apply[X](tx: T[X]): X
  def applicative[X, Tr[_] : Traverse](ms: Tr[T[X]]): Tr[X]
}

trait Augment[T[_], O[_]] {
  def apply[X](tx: T[X]): O[Unit]
}

trait Write[T[_], O] {
  def apply[X](tx: T[X]): O
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy