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

reactify.Var.scala Maven / Gradle / Ivy

The newest version!
package reactify

import java.util.concurrent.atomic.AtomicBoolean

import reactify.bind.{BindSet, Binding}
import reactify.group.VarGroup
import reactify.standard.StandardVar

/**
  * Var represents the combination of `Val` and `Channel` into a stateful and mutable underlying value.
  *
  * @tparam T the type of value this Reactive receives
  */
trait Var[T] extends Val[T] with Channel[T] {
  /**
    * Operating mode of this Var. Defaults to `Normal`
    */
  def mode: Var.Mode

  /**
    * Statically sets a value without monitoring effects
    *
    * @param value the value to assign
    */
  def static(value: T): Unit = set(value)

  /**
    * Group multiple Vars together
    */
  def &(that: Var[T]): Var[T] = and(that)

  /**
    * Group multiple Vars together
    */
  def and(that: Var[T]): Var[T] = VarGroup[T](None, List(this, that))

  /**
    * Functional mapping of this Var into another Var.
    *
    * @param f conversion function
    * @tparam R the type of the new Var
    * @return Var[R]
    */
  override def map[R](f: T => R): Var[R] = {
    val v = Var[R](f(get))
    attach(v := f(_))
    v
  }

  /**
    * Convenience method to create a binding between two `Var`s
    *
    * @param that the second `Var` to bind between
    * @param setNow the `BindSet` value (Defaults to LeftToRight)
    * @param t2v implicit function conversion from T to V
    * @param v2t implicit function conversion from V to T
    * @tparam V the type of the second `Var`
    * @return Binding[T, V]
    */
  def bind[V](that: Var[V], setNow: BindSet = BindSet.LeftToRight)
             (implicit t2v: T => V, v2t: V => T): Binding[T, V] = {
    setNow match {
      case BindSet.LeftToRight => that := t2v(this)
      case BindSet.RightToLeft => this := v2t(that)
      case BindSet.None => // Nothing
    }
    val changing = new AtomicBoolean(false)
    val leftToRight = this.attach { t =>
      if (changing.compareAndSet(false, true)) {
        try {
          that := t2v(get)
        } finally {
          changing.set(false)
        }
      }
    }
    val rightToLeft = that.attach { t =>
      if (changing.compareAndSet(false, true)) {
        try {
          set(v2t(that.get))
        } finally {
          changing.set(false)
        }
      }
    }
    new Binding(this, that, leftToRight, rightToLeft)
  }

  override def toString: String = name.getOrElse("Var")
}

object Var {
  def apply[T](value: => T,
               mode: Mode = Mode.Normal,
               name: Option[String] = None): Var[T] = new StandardVar[T](value, mode, name)

  sealed trait Mode

  object Mode {
    case object Normal extends Mode
    case object Static extends Mode
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy