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

scalaz.effect.ST.scala Maven / Gradle / Ivy

package scalaz
package effect

import reflect.ClassTag

import IvoryTower._
import STRef._
import STArray._
import ST._
import std.function._
import Id._

/**Mutable variable in state thread S containing a value of type A. [[http://research.microsoft.com/en-us/um/people/simonpj/papers/lazy-functional-state-threads.ps.Z]] */
sealed abstract class STRef[S, A] {
  protected var value: A

  /**Reads the value pointed at by this reference. */
  def read: ST[S, A] = returnST(value)

  /**Modifies the value at this reference with the given function. */
  def mod[B](f: A => A): ST[S, STRef[S, A]] = st((s: Tower[S]) => {
    value = f(value);
    (s, this)
  })

  /**Associates this reference with the given value. */
  def write(a: => A): ST[S, STRef[S, A]] = st((s: Tower[S]) => {
    value = a;
    (s, this)
  })

  /**Synonym for write*/
  def |=(a: => A): ST[S, STRef[S, A]] =
    write(a)

  /**Swap the value at this reference with the value at another. */
  def swap(that: STRef[S, A]): ST[S, Unit] = for {
    v1 <- this.read
    v2 <- that.read
    _ <- this write v2
    _ <- that write v1
  } yield ()
}

object STRef extends STRefInstances {

  def apply[S]: (Id ~> STRef[S, ?]) =
    stRef[S]

  def stRef[S]: (Id ~> STRef[S, ?]) = new (Id ~> STRef[S, ?]) {
    def apply[A](a: A) = new STRef[S, A] {
      var value = a
    }
  }
}

sealed abstract class STRefInstances {

  /**Equality for STRefs is reference equality */
  implicit def STRefEqual[S, A]: Equal[STRef[S, A]] =
    Equal.equalA // todo reference equality?
}

/**Mutable array in state thread S containing values of type A. */
sealed abstract class STArray[S, A] {
  def size: Int
  def z: A
  implicit def tag: ClassTag[A]

  private[this] val arr = Need(Array.fill(size)(z))

  private def value: Array[A] = arr.value

  import ST._

  /**Reads the value at the given index. */
  def read(i: Int): ST[S, A] = returnST(value(i))

  /**Writes the given value to the array, at the given offset. */
  def write(i: Int, a: A): ST[S, STArray[S, A]] = st(s => {
    value(i) = a;
    (s, this)
  })

  /**Turns a mutable array into an immutable one which is safe to return. */
  def freeze: ST[S, ImmutableArray[A]] = st(s => (s, ImmutableArray.fromArray(value)))

  /**Fill this array from the given association list. */
  def fill[B](f: (A, B) => A, xs: Traversable[(Int, B)]): ST[S, Unit] = xs match {
    case Nil             => returnST(())
    case ((i, v) :: ivs) => for {
      _ <- update(f, i, v)
      _ <- fill(f, ivs)
    } yield ()
  }

  /**Combine the given value with the value at the given index, using the given function. */
  def update[B](f: (A, B) => A, i: Int, v: B) = for {
    x <- read(i)
    _ <- write(i, f(x, v))
  } yield ()
}

object STArray {
  def apply[S, A : ClassTag](s: Int, a: A): STArray[S, A] = stArray(s, a)

  def stArray[S, A](s: Int, a: A)(implicit t: ClassTag[A]): STArray[S, A] = new STArray[S, A] {
    val size = s
    val z = a
    implicit val tag = t
  }
}

/**
 * Purely functional mutable state threads.
 * Based on JL and SPJ's paper "Lazy Functional State Threads"
 */
sealed abstract class ST[S, A] {
  private[effect] def apply(s: Tower[S]): (Tower[S], A)

  import ST._

  def flatMap[B](g: A => ST[S, B]): ST[S, B] =
    st(s => apply(s) match {
      case (ns, a) => g(a)(ns)
    })

  def map[B](g: A => B): ST[S, B] =
    st(s => apply(s) match {
      case (ns, a) => (ns, g(a))
    })
}

object ST extends STInstances {
  def apply[S, A](a: => A): ST[S, A] =
    returnST(a)

  def st[S, A](f: Tower[S] => (Tower[S], A)): ST[S, A] = new ST[S, A] {
    private[effect] def apply(s: Tower[S]) = f(s)
  }

  // Implicit conversions between IO and ST
  implicit def STToIO[A](st: ST[IvoryTower, A]): IO[A] =
    IO.io(rw => Free.return_(st(rw)))

  /**Put a value in a state thread */
  def returnST[S, A](a: => A): ST[S, A] =
    st(s => (s, a))

  /**Run a state thread */
  def runST[A](f: Forall[ST[?, A]]): A =
    f.apply.apply(ivoryTower)._2

  /**Allocates a fresh mutable reference. */
  def newVar[S]: Id ~> λ[α => ST[S, STRef[S, α]]] =
    new (Id ~> λ[α => ST[S, STRef[S, α]]]) {
      def apply[A](a: A) = returnST(stRef[S](a))
    }

  /**Allocates a fresh mutable array. */
  def newArr[S, A: ClassTag](size: Int, z: A): ST[S, STArray[S, A]] =
    returnST(stArray[S, A](size, z))

  /**Allows the result of a state transformer computation to be used lazily inside the computation. */
  def fixST[S, A](k: (=> A) => ST[S, A]): ST[S, A] = st(s => {
    lazy val ans: (Tower[S], A) = k(r)(s)
    lazy val (_, r) = ans
    ans
  })

  /**Accumulates an integer-associated list into an immutable array. */
  def accumArray[F[_], A : ClassTag, B](size: Int, f: (A, B) => A, z: A, ivs: F[(Int, B)])(implicit F: Foldable[F]): ImmutableArray[A] = {
    import std.anyVal.unitInstance
    type STA[S] = ST[S, ImmutableArray[A]]
    runST(new Forall[STA] {
      def apply[S] = for {
        a <- newArr(size, z)
        _ <- {
          F.foldMap(ivs)((x: (Int, B)) => a.update(f, x._1, x._2))(stMonoid[S, Unit])
        }
        frozen <- a.freeze
      } yield frozen
    })
  }
}

sealed abstract class STInstance0 {
  implicit def stSemigroup[S, A](implicit A: Semigroup[A]): Semigroup[ST[S, A]] =
      Semigroup.liftSemigroup[ST[S, ?], A](ST.stMonad[S], A)
}

sealed abstract class STInstances extends STInstance0 {
  implicit def stMonoid[S, A](implicit A: Monoid[A]): Monoid[ST[S, A]] =
    Monoid.liftMonoid[ST[S, ?], A](stMonad[S], A)

  implicit def stMonad[S]: Monad[ST[S, ?]] =
    new Monad[ST[S, ?]] {
      def point[A](a: => A): ST[S, A] = returnST(a)
      def bind[A, B](fa: ST[S, A])(f: A => ST[S, B]): ST[S, B] = fa flatMap f
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy