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

japgolly.scalajs.react.State.scala Maven / Gradle / Ivy

package japgolly.scalajs.react

import scalaz.{Optional => _, _}
import scalaz.effect.IO
import Scalaz.Id
import Leibniz.===

private[react] object ScalazReactState {
  import ScalazReact._

  final class ScalazReactStateOps[S, W[_]](private val $: CompState.Access[S], W: CallbackTo ~> W) {
    @inline private implicit def autoToW[A](cb: CallbackTo[A]): W[A] = W(cb)

    private def run[M[_], A, B](st: => ReactST[M, S, A], f: (S, S, A, => Callback) => CallbackTo[B])(implicit M: M ~> CallbackTo, N: Monad[M]): CallbackTo[B] =
      $.state |> (StateAndCallbacks(_)) >>= (s1 =>
        M(st run s1) >>= { x2 =>
          val s2 = x2._1
          val a  = x2._2
          f(s1.state, s2.state, a, $.setState(s2.state, s2.cb))
        }
      )

    def runState[M[_], A](st: => ReactST[M, S, A])(implicit M: M ~> CallbackTo, N: Monad[M]): W[A] =
      run[M, A, A](st, (s1,s2,a,cb) => cb.map(_ => a))

    def _runState[I, M[_], A](f: I => ReactST[M, S, A])(implicit M: M ~> CallbackTo, N: Monad[M]): I => W[A] =
      i => runState(f(i))

    def _runState[I, M[_], A](f: I => ReactST[M, S, A], cb: I => Callback)(implicit M: M ~> CallbackTo, N: Monad[M]): I => W[A] =
      i => runState(f(i) addCallback cb(i))

    def runStateF[M[_], A](st: => ReactST[M, S, A])(implicit M: M ~> CallbackTo, N: Monad[M], F: ChangeFilter[S]): W[A] =
      run[M, A, A](st, (s1,s2,a,cb) => F(s1, s2, CallbackTo pure a, _ => cb.map(_ => a)))

    def _runStateF[I, M[_], A](f: I => ReactST[M, S, A])(implicit M: M ~> CallbackTo, N: Monad[M], F: ChangeFilter[S]): I => W[A] =
      i => runStateF(f(i))

    def _runStateF[I, M[_], A](f: I => ReactST[M, S, A], cb: I => Callback)(implicit M: M ~> CallbackTo, N: Monad[M], F: ChangeFilter[S]): I => W[A] =
      i => runStateF(f(i) addCallback cb(i))

    def modStateF(f: S => S, cb: Callback = Callback.empty)(implicit F: ChangeFilter[S]): W[Unit] =
      $.state >>= (s1 =>
        F(s1, f(s1), Callback.empty, $.setState(_, cb)))

    @inline def _modStateF[I](f: I => S => S, cb: Callback = Callback.empty)(implicit F: ChangeFilter[S]): I => W[Unit] =
      i => modStateF(f(i), cb)
  }

  final class SzRExt_StateTOps[M[_], S, A](private val s: StateT[M, S, A]) extends AnyVal {
    @inline def liftS(implicit M: Monad[M]): ReactST[M, S, A] = ReactS.liftS(s)
  }

  final class SzRExt__StateTOps[I, M[_], S, A](private val f: I => StateT[M, S, A]) extends AnyVal {
    @inline def liftS(implicit M: Monad[M]): I => ReactST[M, S, A] =
      i => new SzRExt_StateTOps(f(i)).liftS
  }

  final class SzRExt_ReactSOps[S, A](private val s: ReactS[S,A]) extends AnyVal {
    // Very common case. Very sick of seeing it highlighted red everywhere in Intellij.
    @inline def liftIO: ReactST[IO, S, A] = s.lift[IO]
    @inline def liftCB: ReactST[CallbackTo, S, A] = s.lift[CallbackTo]
  }

  final class SzRExt_ReactSTOps[M[_], S, A](private val s: ReactST[M,S,A]) extends AnyVal {
    def addCallback(c: Callback)(implicit M: Monad[M]): ReactST[M,S,A] =
      s.flatMap(ReactS.callbackT(_, c))

    def addCallbackS(c: S => Callback)(implicit M: Monad[M]): ReactST[M,S,A] =
      s.flatMap(ReactS.callbacksT(_, c))

    // This shouldn't be needed; it's already in BindSyntax.
    def >>[B](t: => ReactST[M,S,B])(implicit M: Monad[M]): ReactST[M,S,B] =
      s.flatMap(_ => t)

    /** zoom2 because StateT.zoom already exists. 2 because it takes two fns. */
    def zoom2[T](f: T => S, g: (T, S) => T)(implicit M: Monad[M]): ReactST[M, T, A] =
      ReactS.zoom(s, f, g)

    def zoomU[T](implicit M: Monad[M], ev: S === Unit): ReactST[M, T, A] =
      ReactS.zoomU[M, T, A](ev.subst[({type λ[σ] = ReactST[M, σ, A]})#λ](s))
  }
}

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

trait ScalazReactState {

  @inline implicit def ScalazReactStateOpsCB[$, S]($: $)(implicit ops: $ => CompState.WriteAccess[S]) =
    new ScalazReactState.ScalazReactStateOps[S, CallbackTo](ops($).accessCB, ScalazReact.callbackToItself)

  @inline implicit def ScalazReactStateOpsDirect[$, S]($: $)(implicit ops: $ => CompState.AccessD[S]) =
    new ScalazReactState.ScalazReactStateOps[S, Id](ops($).accessCB, ScalazReact.scalazIdToCallbackIso.to)

  @inline def StateAndCallbacks[S](s: S, cb: Callback = Callback.empty) =
    new StateAndCallbacks[S](s, cb)

  final class StateAndCallbacks[S](val state: S, val cb: Callback) {
    @inline def withState(s2: S) = new StateAndCallbacks(s2, cb)
    def addCallback(cb2: Callback) = new StateAndCallbacks(state, appendCallbacks(cb, cb2))
    override def toString = s"StateAndCallbacks($state, $cb)"
  }

  private[this] def appendCallbacks(a: Callback, b: Callback): Callback =
    if (a.isEmpty_?)
      b
    else if (b.isEmpty_?)
      a
    else
      a >> b

  final type ReactS[S, A] = ReactST[Id, S, A]
  final type ReactST[M[_], S, A] = StateT[M, StateAndCallbacks[S], A]

  /**
   * M prefix indicates M in args.
   * T prefix indicates we want the result lifted into an M.
   */
  object ReactS {
    @inline def apply    [S,A](f: S => (S, A))        : ReactS[S,A]    = applyM[Id, S, A](f)
    @inline def callback [S,A](a: A, c: Callback)     : ReactS[S,A]    = callbackM[Id, S, A](a, c)
    @inline def callbacks[S,A](a: A, c: S => Callback): ReactS[S,A]    = callbacksM[Id, S, A](a, c)
    @inline def get      [S]                          : ReactS[S,S]    = gets(identity[S])
    @inline def mod      [S]  (f: S => S)             : ReactS[S,Unit] = modM[Id, S](f)
    @inline def ret      [S,A](a: A)                  : ReactS[S,A]    = retM[Id, S, A](a)
    @inline def set      [S]  (s: S)                  : ReactS[S,Unit] = mod((_: S) => s)

    @inline def applyT    [M[_],S,A](f: S => (S, A))         (implicit M: Monad      [M]): ReactST[M,S,A]    = applyM(s ⇒ M point f(s))
    @inline def callbackM [M[_],S,A](ma: M[A], c: Callback)  (implicit M: Monad      [M]): ReactST[M,S,A]    = callbacksM(ma, _ => c)
    @inline def callbackT [M[_],S,A](a: A, c: Callback)      (implicit M: Monad      [M]): ReactST[M,S,A]    = callbackM(M point a, c)
    @inline def callbacksT[M[_],S,A](a: A, c: S => Callback) (implicit M: Monad      [M]): ReactST[M,S,A]    = callbacksM(M point a, c)
    @inline def getT      [M[_],S]                           (implicit M: Applicative[M]): ReactST[M,S,S]    = get.lift[M]
    @inline def getsT     [M[_],S,A](f: S => A)              (implicit M: Monad      [M]): ReactST[M,S,A]    = getsM(s ⇒ M point f(s))
    @inline def liftR     [M[_],S,A](f: S ⇒ ReactST[M, S, A])(implicit M: Monad      [M]): ReactST[M,S,A]    = getT[M,S] flatMap f
    @inline def modT      [M[_],S]  (f: S => S)              (implicit M: Monad      [M]): ReactST[M,S,Unit] = modM(s ⇒ M point f(s))
    @inline def retM      [M[_],S,A](ma: M[A])               (implicit M: Monad      [M]): ReactST[M,S,A]    = getsM[M,S,A](_ ⇒ ma)
    @inline def setM      [M[_],S]  (ms: M[S])               (implicit M: Monad      [M]): ReactST[M,S,Unit] = modM((_: S) ⇒ ms)
    @inline def setT      [M[_],S]  (s: S)                   (implicit M: Monad      [M]): ReactST[M,S,Unit] = setM(M point s)

    def applyM[M[_], S, A](f: S => M[(S, A)])(implicit F: Monad[M]): ReactST[M, S, A] =
      StateT[M, StateAndCallbacks[S], A](sc => F.map(f(sc.state))(x => (sc withState x._1, x._2) ))

    def callbacksM[M[_], S, A](ma: M[A], c: S => Callback)(implicit M: Monad[M]): ReactST[M, S, A] =
      StateT[M, StateAndCallbacks[S], A](s => M.map(ma)(a => (s addCallback c(s.state), a)))

    @inline def gets[S, A](f: S => A): ReactS[S, A] =
      State.gets[StateAndCallbacks[S], A](s => f(s.state))

    def getsM[M[_], S, A](f: S => M[A])(implicit F: Monad[M]): ReactST[M, S, A] =
      StateT[M, StateAndCallbacks[S], A](sc => F.map(f(sc.state))((sc, _)))

    def liftS[M[_], S, A](t: StateT[M, S, A])(implicit M: Monad[M]): ReactST[M, S, A] =
      StateT[M, StateAndCallbacks[S], A](sc => M.map(t(sc.state))(sa => (sc withState sa._1, sa._2) ))

    def modM[M[_], S](f: S => M[S])(implicit M: Monad[M]): ReactST[M, S, Unit] =
      StateT[M, StateAndCallbacks[S], Unit](sc => M.map(f(sc.state))(s2 => (sc withState s2,()) ))

    @inline def retT[M[_], S, A](a: A)(implicit M: Monad[M]): ReactST[M, S, A] =
      StateT[M, StateAndCallbacks[S], A](s => M.point((s, a)))

    def unlift[M[_], S, A](t: ReactST[M, S, A])(implicit M: Monad[M]): StateT[M, S, A] =
      StateT[M, S, A](s => M.map(t(StateAndCallbacks(s)))(sa => (sa._1.state, sa._2) ))

    def zoom[M[_], S, T, A](r: ReactST[M, S, A], f: T => S, g: (T, S) => T)(implicit M: Monad[M]): ReactST[M, T, A] =
      StateT[M, StateAndCallbacks[T], A](tc => {
        val m = r(StateAndCallbacks(f(tc.state), tc.cb))
        M.map(m){ case (sc, a) => (StateAndCallbacks(g(tc.state, sc.state), sc.cb), a) }
      })

    @inline def zoomU[M[_], S, A](r: ReactST[M, Unit, A])(implicit M: Monad[M]): ReactST[M, S, A] =
      zoom[M, Unit, S, A](r, _ => (), (s, _) => s)

    /**
     * M prefix indicates M in args.
     * T prefix indicates we want the result lifted into an M.
     */
    @inline def Fix[S] = new Fix[S]
    final class Fix[S] {
      type T[A] = ReactS[S, A]

      @inline def nop :        ReactS[S,Unit] = ret(())
      @inline def _nop: Any => ReactS[S,Unit] = _ => nop

      @inline def apply    [A]     (f: S => (S, A))           : ReactS[S,A]    = ReactS(f)
      @inline def callback [A]     (a: A, c: Callback)        : ReactS[S,A]    = ReactS.callback(a, c)
      @inline def callbacks[A]     (a: A, c: S => Callback)   : ReactS[S,A]    = ReactS.callbacks(a, c)
      @inline def get                                         : ReactS[S,S]    = ReactS.get
      @inline def gets     [A]     (f: S => A)                : ReactS[S,A]    = ReactS.gets(f)
      @inline def mod              (f: S => S)                : ReactS[S,Unit] = ReactS.mod(f)
      @inline def ret      [A]     (a: A)                     : ReactS[S,A]    = ReactS.ret(a)
      @inline def set              (s: S)                     : ReactS[S,Unit] = ReactS.set(s)
      @inline def zoom     [T,A]   (r: ReactS[T, A])
                                   (f: S => T, g: (S, T) => S): ReactS[S,A]    = ReactS.zoom(r, f, g)
      @inline def zoomU    [A]     (r: ReactS[Unit, A])       : ReactS[S,A]    = ReactS.zoomU(r)

      @inline def applyM    [M[_],A](f: S => M[(S, A)])        (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.applyM(f)
      @inline def applyT    [M[_],A](f: S => (S, A))           (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.applyT(f)
      @inline def callbackM [M[_],A](a: M[A], c: Callback)     (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.callbackM(a, c)
      @inline def callbackT [M[_],A](a: A, c: Callback)        (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.callbackT(a, c)
      @inline def callbacksM[M[_],A](a: M[A], c: S => Callback)(implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.callbacksM(a, c)
      @inline def callbacksT[M[_],A](a: A, c: S => Callback)   (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.callbacksT(a, c)
      @inline def getT      [M[_]]                             (implicit M: Applicative[M]): ReactST[M,S,S]    = ReactS.getT
      @inline def getsM     [M[_],A](f: S => M[A])             (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.getsM(f)
      @inline def getsT     [M[_],A](f: S => A)                (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.getsT(f)
      @inline def liftR     [M[_],A](f: S => ReactST[M, S, A]) (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.liftR(f)
      @inline def liftS     [M[_],A](t: StateT[M, S, A])       (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.liftS(t)
      @inline def modM      [M[_]]  (f: S => M[S])             (implicit M: Monad      [M]): ReactST[M,S,Unit] = ReactS.modM(f)
      @inline def modT      [M[_]]  (f: S => S)                (implicit M: Monad      [M]): ReactST[M,S,Unit] = ReactS.modT(f)
      @inline def retM      [M[_],A](ma: M[A])                 (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.retM(ma)
      @inline def retT      [M[_],A](a: A)                     (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.retT(a)
      @inline def setM      [M[_]]  (ms: M[S])                 (implicit M: Monad      [M]): ReactST[M,S,Unit] = ReactS.setM(ms)
      @inline def setT      [M[_]]  (s: S)                     (implicit M: Monad      [M]): ReactST[M,S,Unit] = ReactS.setT(s)
      @inline def unlift    [M[_],A](t: ReactST[M, S, A])      (implicit M: Monad      [M]): StateT [M,S,A]    = ReactS.unlift(t)
    }

    /**
     * T prefix indicates we want the result lifted into an M.
     */
    @inline def FixT[M[_], S] = new FixT[M, S]
    @inline def FixCB[S] = new FixT[CallbackTo, S]
    final class FixT[M[_], S] {
      type T[A] = ReactST[M, S, A]

      @inline def nop (implicit M: Monad[M]):        ReactST[M,S,Unit] = retT(())
      @inline def _nop(implicit M: Monad[M]): Any => ReactST[M,S,Unit] = _ => nop

      @inline def apply     [A]  (f: S => M[(S, A)])        (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.applyM(f)
      @inline def applyT    [A]  (f: S => (S, A))           (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.applyT(f)
      @inline def callback  [A]  (a: M[A], c: Callback)     (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.callbackM(a, c)
      @inline def callbackT [A]  (a: A, c: Callback)        (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.callbackT(a, c)
      @inline def callbacks [A]  (a: M[A], c: S => Callback)(implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.callbacksM(a, c)
      @inline def callbacksT[A]  (a: A, c: S => Callback)   (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.callbacksT(a, c)
      @inline def get                                       (implicit M: Applicative[M]): ReactST[M,S,S]    = ReactS.getT
      @inline def gets      [A]  (f: S => M[A])             (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.getsM(f)
      @inline def getsT     [A]  (f: S => A)                (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.getsT(f)
      @inline def liftR     [A]  (f: S => ReactST[M, S, A]) (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.liftR(f)
      @inline def liftS     [A]  (t: StateT[M, S, A])       (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.liftS(t)
      @inline def mod            (f: S => M[S])             (implicit M: Monad      [M]): ReactST[M,S,Unit] = ReactS.modM(f)
      @inline def modT           (f: S => S)                (implicit M: Monad      [M]): ReactST[M,S,Unit] = ReactS.modT(f)
      @inline def ret       [A]  (ma: M[A])                 (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.retM(ma)
      @inline def retT      [A]  (a: A)                     (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.retT(a)
      @inline def set            (ms: M[S])                 (implicit M: Monad      [M]): ReactST[M,S,Unit] = ReactS.setM(ms)
      @inline def setT           (s: S)                     (implicit M: Monad      [M]): ReactST[M,S,Unit] = ReactS.setT(s)
      @inline def unlift    [A]  (t: ReactST[M, S, A])      (implicit M: Monad      [M]): StateT [M,S,A]    = ReactS.unlift(t)
      @inline def zoom      [T,A](r: ReactST[M, T, A])
                                 (f: S => T, g: (S,T) => S) (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.zoom(r, f, g)
      @inline def zoomU     [A]  (r: ReactST[M, Unit, A])   (implicit M: Monad      [M]): ReactST[M,S,A]    = ReactS.zoomU(r)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy