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

japgolly.scalajs.react.util.Effect.scala Maven / Gradle / Ivy

The newest version!
package japgolly.scalajs.react.util

import japgolly.scalajs.react.util.Util.identityFn
import scala.annotation.tailrec
import scala.scalajs.js
import scala.util.Try

trait Effect[F[_]] extends Monad[F] {
  def delay      [A]         (a: => A)                           : F[A]
  def handleError[A, AA >: A](fa: => F[A])(f: Throwable => F[AA]): F[AA]
  def suspend    [A]         (fa: => F[A])                       : F[A]

  /** Wraps this callback in a `try-finally` block and runs the given callback in the `finally` clause, after the
    * current callback completes, be it in error or success.
    */
  def finallyRun[A, B](fa: => F[A], runFinally: => F[B]): F[A]

  @inline final def throwException[A](t: Throwable): F[A] =
    delay(throw t)
}

object Effect extends EffectFallbacks {

  trait WithDefaults[F[_]] extends Effect[F] {
  }

  trait Dispatch[F[_]] extends Effect[F] {
    /** Fire and forget, could be sync or async */
    def dispatch[A](fa: F[A]): Unit
    def dispatchFn[A](fa: => F[A]): js.Function0[Unit]
  }

  object Dispatch {
    trait WithDefaults[F[_]] extends Dispatch[F] with Effect.WithDefaults[F] {
      override def dispatchFn[A](fa: => F[A]): js.Function0[Unit] =
        () => dispatch(fa)
    }
  }

  // ===================================================================================================================

  type Id[A] = A

  trait UnsafeSync[F[_]] extends Dispatch[F] {
    def runSync     [A]   (fa: => F[A])                     : A
    def sequence_   [A]   (fas: => Iterable[F[A]])          : F[Unit]
    def sequenceList[A]   (fas: => List[F[A]])              : F[List[A]]
    def toJsFn      [A]   (fa: => F[A])                     : js.Function0[A]
    def traverse_   [A, B](as: => Iterable[A])(f: A => F[B]): F[Unit]
    def traverseList[A, B](as: => List[A])(f: A => F[B])    : F[List[B]]
    def when_       [A]   (cond: => Boolean)(fa: => F[A])   : F[Unit]

    @inline final def unless_[A](cond: => Boolean)(fa: => F[A]): F[Unit] =
      when_(!cond)(fa)

    final def subst[G[_], X[_[_]]](xg: X[G])(xf: => X[F])(implicit g: UnsafeSync[G]): X[F] =
      if (this eq g) xg.asInstanceOf[X[F]] else xf

    final def transSync[G[_], A](ga: => G[A])(implicit g: UnsafeSync[G]): F[A] =
      if (this eq g)
        ga.asInstanceOf[F[A]]
      else
        delay(g.runSync(ga))

    final def transSyncFn1[G[_], A, B](f: A => G[B])(implicit g: UnsafeSync[G]): A => F[B] =
      if (this eq g)
        f.asInstanceOf[A => F[B]]
      else
        a => delay(g.runSync(f(a)))

    final def transSyncFn2[G[_], A, B, C](f: (A, B) => G[C])(implicit g: UnsafeSync[G]): (A, B) => F[C] =
      if (this eq g)
        f.asInstanceOf[(A, B) => F[C]]
      else
        (a, b) => delay(g.runSync(f(a, b)))

    final def transSyncFn2C[G[_], A, B, C](f: (A, G[B]) => G[C])(implicit g: UnsafeSync[G]): (A, F[B]) => F[C] =
      if (this eq g)
        f.asInstanceOf[(A, F[B]) => F[C]]
      else
        (a, gb) => delay(g.runSync(f(a, g.transSync(gb)(this))))

    final def transDispatch[G[_]](f: => G[Unit])(implicit g: Dispatch[G]): F[Unit] =
      if (this eq g)
        f.asInstanceOf[F[Unit]]
      else
        delay(g.dispatch(f))

    final def transDispatchFn1[G[_], A](f: A => G[Unit])(implicit g: Dispatch[G]): A => F[Unit] =
      if (this eq g)
        f.asInstanceOf[A => F[Unit]]
      else
        a => delay(g.dispatch(f(a)))

    final def transDispatchFn3[G[_], A, B, C](f: (A, B, C) => G[Unit])(implicit g: Dispatch[G]): (A, B, C) => F[Unit] =
      if (this eq g)
        f.asInstanceOf[(A, B, C) => F[Unit]]
      else
        (a, b, c) => delay(g.dispatch(f(a, b, c)))
  }

  object UnsafeSync {

    trait WithDefaults[F[_]] extends UnsafeSync[F] with Dispatch.WithDefaults[F] {
      override def toJsFn[A](f: => F[A]): js.Function0[A] =
        () => runSync(f)

      override def suspend[A](fa: => F[A]): F[A] =
        flatMap(delay(fa))(identityFn)
    }

    implicit lazy val id: UnsafeSync[Id] = new WithDefaults[Id] {

      override def delay       [A]   (a: => A)                       = a
      override def dispatch    [A]   (a: A)                          = ()
      override def finallyRun  [A, B](a: => A, runFinally: => B)     = try a finally runFinally
      override def flatMap     [A, B](a: A)(f: A => B)               = f(a)
      override def map         [A, B](a: A)(f: A => B)               = f(a)
      override def pure        [A]   (a: A)                          = a
      override def runSync     [A]   (a: => A)                       = a
      override def sequenceList[A]   (as: => List[A])                = as
      override def traverse_   [A, B](as: => Iterable[A])(f: A => B) = as.foreach(f)
      override def traverseList[A, B](as: => List[A])(f: A => B)     = as.map(f)
      override def when_       [A]   (cond: => Boolean)(a: => A)     = if (cond) a

      override def tailrec[A, B](a: A)(f: A => Either[A, B]): B = {
        @tailrec
        def go(a: A): B =
          f(a) match {
            case Left(n)  => go(n)
            case Right(b) => b
          }
        go(a)
      }

      override def handleError[A, AA >: A](a: => A)(f: Throwable => AA): AA =
        try a catch { case t: Throwable => f(t) }

      override def sequence_[A](as: => Iterable[A]): Unit = {
        val i = as.iterator
        while (i.hasNext)
          i.next()
      }
    }
  }

  // ===================================================================================================================

  trait Sync[F[_]] extends UnsafeSync[F] {

    val empty: F[Unit]
    val semigroupSyncOr: Semigroup[F[Boolean]]
    implicit val semigroupSyncUnit: Semigroup[F[Unit]]

    def fromJsFn0[A](f: js.Function0[A]): F[A]
    def isEmpty  [A](f: F[A])           : Boolean
    def reset    [A](fa: F[A])          : F[Unit]
    def runAll   [A](callbacks: F[A]*)  : F[Unit]
  }

  object Sync {
    type Untyped[A] = js.Function0[A]

    trait WithDefaultDispatch[F[_]] extends Sync[F] with Dispatch.WithDefaults[F] {
      override def dispatch[A](fa: F[A]): Unit =
        if (!isEmpty(fa))
          runSync(fa)
    }

    trait WithDefaults[F[_]] extends WithDefaultDispatch[F] with UnsafeSync.WithDefaults[F] {

      override def fromJsFn0[A](f: js.Function0[A]) =
        delay(f())

      override def runAll[A](callbacks: F[A]*): F[Unit] =
        callbacks.foldLeft(empty)((x, y) => chain(x, reset(y)))

      /** Wraps this callback in a `try-finally` block and runs the given callback in the `finally` clause, after the
        * current callback completes, be it in error or success.
        */
      override def finallyRun[A, B](fa: => F[A], runFinally: => F[B]): F[A] =
        delay { try runSync(fa) finally runSync(runFinally) }

      override def reset[A](fa: F[A]): F[Unit] =
        delay(
          try
            runSync(fa): Unit
          catch {
            case t: Throwable =>
              t.printStackTrace()
          }
        )

      override def traverse_[A, B](as: => Iterable[A])(f: A => F[B]): F[Unit] =
        delay {
          for (a <- as)
            runSync(f(a))
        }

      override def sequence_[A](fas: => Iterable[F[A]]): F[Unit] =
        traverse_(fas)(identityFn)

      override def traverseList[A, B](as: => List[A])(f: A => F[B]): F[List[B]] =
        delay {
          val l = List.newBuilder[B]
          for (a <- as)
            l += runSync(f(a))
          l.result()
        }

      override def sequenceList[A](fas: => List[F[A]]): F[List[A]] =
        traverseList(fas)(identityFn)

      implicit val semigroupSyncUnit: Semigroup[F[Unit]] =
        Semigroup((f, g) => flatMap(f)(_ => g))

      val semigroupSyncOr: Semigroup[F[Boolean]] = {
        val True = pure(true)
        Semigroup((x, y) => flatMap(x)(b => if (b) True else y))
      }

      override def when_[A](cond: => Boolean)(fa: => F[A]): F[Unit] =
        delay {
          if (cond)
            runSync(fa)
        }

      override def handleError[A, AA >: A](fa: => F[A])(f: Throwable => F[AA]): F[AA] =
        delay[AA](
          try
            runSync(fa)
          catch {
            case t: Throwable => runSync(f(t))
          }
        )
    }
  }

  // ===================================================================================================================

  trait Async[F[_]] extends Dispatch[F] {
    def async        [A](f: Async.Untyped[A])  : F[A]
    def first        [A](f: Async.Untyped[A])  : F[A]
    def runAsync     [A](fa: => F[A])          : Async.Untyped[A]
    def toJsPromise  [A](fa: => F[A])          : () => js.Promise[A]
    def fromJsPromise[A](pa: => js.Thenable[A]): F[A]

    def async_(onCompletion: Sync.Untyped[Unit] => Sync.Untyped[Unit]): F[Unit]

    final def subst[G[_], X[_[_]]](xg: X[G])(xf: => X[F])(implicit g: Async[G]): X[F] =
      if (this eq g) xg.asInstanceOf[X[F]] else xf

    final def transAsync[G[_], A](ga: => G[A])(implicit g: Async[G]): F[A] =
      if (this eq g)
        ga.asInstanceOf[F[A]]
      else
        async(g.runAsync(ga)(_))
  }

  object Async {
    type Untyped[A] = (Try[A] => Sync.Untyped[Unit]) => Sync.Untyped[Unit]

    trait WithDefaults[F[_]] extends Async[F] with Dispatch.WithDefaults[F] {
      override def first[A](f: Async.Untyped[A]): F[A] =
        async(g => () => {
          var first = true
          f(ea => () => {
            if (first) {
              first = false
              g(ea).apply()
            }
          }).apply()
        })

      override def dispatch[A](fa: F[A]): Unit =
        toJsPromise(fa).apply(): Unit

      override def toJsPromise[A](fa: => F[A]): () => js.Promise[A] =
        JsUtil.asyncToPromise(runAsync(fa))

      override def fromJsPromise[A](pa: => js.Thenable[A]): F[A] =
        async(f => () => JsUtil.runPromiseAsync(pa)(f))
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy