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

rescala.compat.EventCompatBundle.scala Maven / Gradle / Ivy

There is a newer version: 0.35.1
Show newest version
package rescala.compat

import rescala.core.Core
import rescala.interface.RescalaInterface
import rescala.operator.{Operators, Pulse, cutOutOfUserComputation}
import rescala.macros.ReadableMacroBundle

trait EventCompatBundle extends ReadableMacroBundle {
  bundle: Operators =>

  trait EventCompat[+T] extends ReadableMacro[Option[T]] {
    selfType: Event[T] =>

    /** Filters the event, only propagating the value when the filter is true.
      * @group operator
      */
    @cutOutOfUserComputation
    final inline def filter(inline expression: T => Boolean)(implicit ticket: CreationTicket): Event[T] =
      Event.dynamic { this.value.filter(expression) }

    /** Filters the event, only propagating the value when the filter is true.
      * @group operator
      */
    @cutOutOfUserComputation
    final infix inline def &&(inline expression: T => Boolean)(implicit ticket: CreationTicket): Event[T] =
      Event.dynamic { this.value.filter(expression) }

    /** Collects the results from a partial function
      * @group operator
      */
    final inline def collect[U](inline expression: PartialFunction[T, U])(implicit ticket: CreationTicket): Event[U] =
      Event.dynamic { this.value.collect(expression) }

    /** Transform the event.
      * @group operator
      */
    @cutOutOfUserComputation
    final inline def map[B](inline expression: T => B)(implicit ticket: CreationTicket): Event[B] =
      Event.dynamic { this.value.map(expression) }

    /** Folds events with a given operation to create a Signal.
      * @group conversion
      * @inheritdoc
      */
    @cutOutOfUserComputation
    final def fold[A](init: A)(op: (A, T) => A)(implicit ticket: CreationTicket): Signal[A] =
      Events.foldOne(this, init)(op)

  }

  /** Similar to [[Signal]] expressions, but resulting in an event.
    * Accessed events return options depending on whether they fire or not,
    * and the complete result of the expression is an event as well.
    *
    * @see [[Signal]]
    * @group create
    */
  object Event {
    inline def apply[T](inline expr: Option[T])(using ct: CreationTicket): Event[T] = {
      val (sources, fun, isStatic) = rescala.macros.getDependencies[Option[T], ReSource, StaticTicket, true](expr)
      bundle.Events.static(sources: _*)(fun)
    }
    inline def dynamic[T](inline expr: Option[T])(using ct: CreationTicket): Event[T] = {
      val (sources, fun, isStatic) = rescala.macros.getDependencies[Option[T], ReSource, DynamicTicket, false](expr)
      bundle.Events.dynamic(sources: _*)(fun)
    }
  }

  object Fold {
    inline def branch[T](inline expr: FoldState[T] ?=> T): Branch[T] = {
      val (sources, fun, isStatic) =
        rescala.macros.getDependencies[FoldState[T] ?=> T, ReSource, DynamicTicket, false](expr)
      Branch(sources, false, fun)
    }

    class Branch[S](
        val staticDependencies: List[ReSource],
        val isStatic: Boolean,
        val run: DynamicTicket => FoldState[S] ?=> S
    )

    def apply[T](init: T)(branches: Branch[T]*)(using ticket: CreationTicket): Signal[T] = {

      val staticDeps = branches.iterator.flatMap(_.staticDependencies).toSet
      val isStatic   = branches.forall(_.isStatic)

      def operator(dt: DynamicTicket, state: () => T): T =
        branches.foldLeft(state()) { (curr, b) => b.run(dt)(using FoldState(curr)) }

      ticket.create(
        staticDeps,
        Pulse.tryCatch[T](Pulse.Value(init)),
        needsReevaluation = false
      ) { state => new SignalImpl[T](state, operator, ticket.rename, if isStatic then None else Some(staticDeps)) }
    }
  }

  inline def current[S](using fs: FoldState[S]): S = FoldState.unwrap(fs)

  extension [T](e: Event[T]) {
    inline def act[S](inline f: FoldState[S] ?=> T => S): Fold.Branch[S] = Fold.branch { e.value.fold(current)(f) }
  }

}

opaque type FoldState[T] = T
object FoldState {
  def unwrap[T](fs: FoldState[T]): T = fs
  def apply[T](t: T): FoldState[T]   = t
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy