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

diode.ModelRW.scala Maven / Gradle / Ivy

The newest version!
package diode

import scala.language.higherKinds


/**
  * A read-only version of ModelR that doesn't know about the root model.
  *
  * @tparam S Type of the reader value
  */
trait ModelRO[S] {
  /**
    * Type of a new reader returned by functions such as `zoom`
    * @tparam T
    */
  type NewR[T] <: ModelRO[T]

  /**
    * Returns the value of the reader
    */
  def value: S

  /**
    * Returns the value of the reader
    */
  def apply(): S = value

  /**
    * Checks if `that` is equal to `this` using an appropriate equality check
    *
    * @param that Value to compare with
    * @return
    */
  def ===(that: S): Boolean

  def =!=(that: S): Boolean = ! ===(that)

  /**
    * Zooms into the model using the provided accessor function
    *
    * @param get Function to go from current reader to a new value
    */
  def zoom[T](get: S => T)(implicit feq: FastEq[_ >: T]): NewR[T]

  /**
    * Maps over current reader into a new value provided by `f`. Reader type `S` must be of type `F[A]`,
    * for example `Option[A]`.
    *
    * @param f The function to apply
    */
  def map[F[_], A, B](f: A => B)
    (implicit ev: S =:= F[A], monad: Monad[F], feq: FastEq[_ >: B]): NewR[F[B]] =
    zoomMap((_: S) => ev(value))(f)

  /**
    * FlatMaps over current reader into a new value provided by `f`. Reader type `S` must be of type `F[A]`,
    * for example `Option[A]`.
    *
    * @param f The function to apply, must return a value of type `F[B]`
    */
  def flatMap[F[_], A, B](f: A => F[B])
    (implicit ev: S =:= F[A], monad: Monad[F], feq: FastEq[_ >: B]): NewR[F[B]] =
    zoomFlatMap((_: S) => ev(value))(f)

  /**
    * Zooms into the model and maps over the zoomed value, which must be of type `F[A]`
    *
    * @param fa Zooming function
    * @param f  The function to apply
    */
  def zoomMap[F[_], A, B](fa: S => F[A])(f: A => B)
    (implicit monad: Monad[F], feq: FastEq[_ >: B]): NewR[F[B]]

  /**
    * Zooms into the model and flatMaps over the zoomed value, which must be of type `F[A]`
    *
    * @param fa Zooming function
    * @param f  The function to apply, must return a value of type `F[B]`
    */
  def zoomFlatMap[F[_], A, B](fa: S => F[A])(f: A => F[B])
    (implicit monad: Monad[F], feq: FastEq[_ >: B]): NewR[F[B]]
}

/**
  * Base trait for all model readers
  *
  * @tparam M Type of the base model
  * @tparam S Type of the reader value
  */
trait ModelR[M, S] extends ModelRO[S] {
  override type NewR[T] = ModelR[M, T]

  /**
    * Evaluates the reader against a supplied `model`
    */
  def eval(model: M): S

  /**
    * Returns the root model reader of this reader
    */
  def root: ModelR[M, M]

  /**
    * Combines this reader with another reader to provide a new reader returning a tuple of the values
    * of the two original readers.
    *
    * @param that The other reader
    */
  def zip[SS](that: ModelR[M, SS])(implicit feqS: FastEq[_ >: S], feqSS: FastEq[_ >: SS]): ModelR[M, (S, SS)]
}

/**
  * Base trait for all model writers
  *
  * @tparam M Type of the base model
  * @tparam S Type of the reader/writer value
  */
trait ModelRW[M, S] extends ModelR[M, S] {
  /**
    * Updates the model using the value provided and returns the updated model.
    */
  def updated(newValue: S): M

  /**
    * Updates the supplied model with the value provided and returns the updated model.
    */
  def updatedWith(model: M, newValue: S): M

  /**
    * Zooms into the model using the provided `get` function. The `set` function is used to
    * update the model with a new value.
    *
    * @param get Function to go from current reader to a new value
    * @param set Function to update the model with a new value
    */
  def zoomRW[T](get: S => T)(set: (S, T) => S)(implicit feq: FastEq[_ >: T]): ModelRW[M, T]

  /**
    * Zooms into the model and maps over the zoomed value, which must be of type `F[A]`. The `set` function is used to
    * update the model with a new value.
    *
    * @param fa  Zooming function
    * @param f   The function to apply
    * @param set Function to update the model with a new value
    */
  def zoomMapRW[F[_], A, B](fa: S => F[A])(f: A => B)(set: (S, F[B]) => S)
    (implicit monad: Monad[F], feq: FastEq[_ >: B]): ModelRW[M, F[B]]

  /**
    * Zooms into the model and flatMaps over the zoomed value, which must be of type `F[A]`. The `set` function is used to
    * update the model with a new value.
    *
    * @param fa  Zooming function
    * @param f   The function to apply
    * @param set Function to update the model with a new value
    */
  def zoomFlatMapRW[F[_], A, B](fa: S => F[A])(f: A => F[B])(set: (S, F[B]) => S)
    (implicit monad: Monad[F], feq: FastEq[_ >: B]): ModelRW[M, F[B]]
}

/**
  * Implements common functionality for all model readers
  *
  * @tparam M Type of the base model
  * @tparam S Type of the reader value
  */
trait BaseModelR[M, S] extends ModelR[M, S] {
  override def eval(model: M): S

  override def value = eval(root.value)

  override def zoom[T](get: S => T)(implicit feq: FastEq[_ >: T]) =
    new ZoomModelR[M, T](root, get compose this.eval)

  override def zip[SS](that: ModelR[M, SS])(implicit feqS: FastEq[_ >: S], feqSS: FastEq[_ >: SS]) =
    new ZipModelR[M, S, SS](root, eval, that.eval)

  override def zoomMap[F[_], A, B](fa: S => F[A])(f: A => B)
    (implicit monad: Monad[F], feq: FastEq[_ >: B]): ModelR[M, F[B]] =
    new MapModelR(root, fa compose eval, f)

  override def zoomFlatMap[F[_], A, B](fa: S => F[A])(f: A => F[B])
    (implicit monad: Monad[F], feq: FastEq[_ >: B]): ModelR[M, F[B]] =
    new FlatMapModelR(root, fa compose eval, f)
}

/**
  * Model reader for the root value
  */
class RootModelR[M <: AnyRef](get: => M) extends BaseModelR[M, M] {
  def root = this

  override def eval(model: M) = get

  override def value = get

  override def ===(that: M): Boolean = this eq that
}

/**
  * Model reader for a zoomed value
  */
class ZoomModelR[M, S](val root: ModelR[M, M], get: M => S)
  (implicit feq: FastEq[_ >: S]) extends BaseModelR[M, S] {
  override def eval(model: M) = get(model)

  override def ===(that: S): Boolean = feq.eqv(value, that)
}

trait MappedModelR[F[_], M, B] {
  self: ModelR[M, F[B]] =>
  protected def monad: Monad[F]
  protected def feq: FastEq[_ >: B]
  protected def mapValue: F[B]

  private var memoized = mapValue

  override def eval(model: M): F[B] = {
    val v = mapValue
    // update memoized value only when the value inside the monad changes
    if (!monad.isEqual(v, memoized)(feq.eqv)) {
      memoized = v
    }
    memoized
  }

  override def ===(that: F[B]) = monad.isEqual(value, that)(feq.eqv)
}

/**
  * Model reader for a mapped value
  */
class MapModelR[F[_], M, A, B](val root: ModelR[M, M], get: M => F[A], f: A => B)
  (implicit val monad: Monad[F], val feq: FastEq[_ >: B])
  extends BaseModelR[M, F[B]] with MappedModelR[F, M, B] {

  override protected def mapValue = monad.map(get(root.value))(f)
}

/**
  * Model reader for a flatMapped value
  */
class FlatMapModelR[F[_], M, A, B](val root: ModelR[M, M], get: M => F[A], f: A => F[B])
  (implicit val monad: Monad[F], val feq: FastEq[_ >: B])
  extends BaseModelR[M, F[B]] with MappedModelR[F, M, B] {

  override protected def mapValue = monad.flatMap(get(root.value))(f)
}

/**
  * Model reader for two zipped readers
  */
class ZipModelR[M, S, SS](val root: ModelR[M, M], get1: M => S, get2: M => SS)
  (implicit feqS: FastEq[_ >: S], feqSS: FastEq[_ >: SS])
  extends BaseModelR[M, (S, SS)] {
  // initial value for zipped
  private var zipped = (get1(root.value), get2(root.value))

  // ZipModel uses optimized `get` functions to check if the contents of the tuple has changed or not
  override def eval(model: M) = {
    // check if inner references have changed
    val v1 = get1(root.value)
    val v2 = get2(root.value)
    if (feqS.neqv(zipped._1, v1) || feqSS.neqv(zipped._2, v2)) {
      // create a new tuple
      zipped = (v1, v2)
    }
    zipped
  }

  override def ===(that: (S, SS)) = zipped eq that
}

/**
  * Implements common functionality for all reader/writers
  *
  * @tparam M Type of the base model
  * @tparam S Type of the reader/writer value
  */
trait BaseModelRW[M, S] extends ModelRW[M, S] with BaseModelR[M, S] {
  override def zoomRW[U](get: S => U)(set: (S, U) => S)(implicit feq: FastEq[_ >: U]) =
    new ZoomModelRW[M, U](root, get compose eval, (s, u) => updatedWith(s, set(eval(s), u)))

  override def zoomMapRW[F[_], A, B](fa: S => F[A])(f: A => B)(set: (S, F[B]) => S)
    (implicit monad: Monad[F], feq: FastEq[_ >: B]) =
    new MapModelRW(root, fa compose eval, f)((s, u) => updatedWith(s, set(eval(s), u)))

  override def zoomFlatMapRW[F[_], A, B](fa: S => F[A])(f: A => F[B])(set: (S, F[B]) => S)
    (implicit monad: Monad[F], feq: FastEq[_ >: B]) =
    new FlatMapModelRW(root, fa compose eval, f)((s, u) => updatedWith(s, set(eval(s), u)))

  override def updated(newValue: S) = updatedWith(root.value, newValue)
}

/**
  * Model reader/writer for the root value
  */
class RootModelRW[M <: AnyRef](get: => M) extends RootModelR(get) with BaseModelRW[M, M] {
  override def updatedWith(model: M, value: M) = value

  // override for root because it's a simpler case
  override def zoomRW[T](get: M => T)(set: (M, T) => M)(implicit feq: FastEq[_ >: T]) =
    new ZoomModelRW[M, T](this, get, set)
}

/**
  * Model reader/writer for a zoomed value
  */
class ZoomModelRW[M, S](root: ModelR[M, M], get: M => S, set: (M, S) => M)
  (implicit feq: FastEq[_ >: S]) extends ZoomModelR(root, get) with BaseModelRW[M, S] {
  override def updatedWith(model: M, value: S) = set(model, value)
}

/**
  * Model reader/writer for a mapped value
  */
class MapModelRW[F[_], M, A, B](root: ModelR[M, M], get: M => F[A], f: A => B)(set: (M, F[B]) => M)
  (implicit monad: Monad[F], feq: FastEq[_ >: B])
  extends MapModelR(root, get, f) with BaseModelRW[M, F[B]] {
  override def updatedWith(model: M, value: F[B]) = set(model, value)
}

/**
  * Model reader/writer for a flatMapped value
  */
class FlatMapModelRW[F[_], M, A, B](root: ModelR[M, M], get: M => F[A], f: A => F[B])(set: (M, F[B]) => M)
  (implicit monad: Monad[F], feq: FastEq[_ >: B])
  extends FlatMapModelR(root, get, f) with BaseModelRW[M, F[B]] {
  override def updatedWith(model: M, value: F[B]) = set(model, value)
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy