package scalaz
package effects
import Scalaz._
import Free._
sealed trait IO[A] {
private[effects] def apply(rw: World[RealWorld]): Trampoline[(World[RealWorld], A)]
def unsafePerformIO: A = apply(realWorld).run._2
def unsafeInterleaveIO: IO[Trampoline[A]] = io(apply(realWorld).map(_._2))
def unsafeZipWith[B, C](iob: IO[B], f: (A, B) => C): IO[C] = (for {
a <- unsafeInterleaveIO
b <- iob.unsafeInterleaveIO
c <- IO(rw => a zipWith (b, (x: A, y: B) => (rw -> f(x, y))))
} yield c)
def flatMap[B](f: A => IO[B]): IO[B] = IO(rw =>
apply(rw) flatMap {
case (nw, a) => f(a)(nw)
})
def map[B](f: A => B): IO[B] = IO(rw =>
apply(rw) map {
case (nw, a) => (nw, f(a))
})
def except(handler: Throwable => IO[A]): IO[A] =
IO(rw => try { Return(this(rw).run) } catch { case e => handler(e)(rw) })
def catchSome[B](p: Throwable => Option[B], handler: B => IO[A]): IO[A] =
except(e => p(e) cata (handler, throw e))
def catchLeft: IO[Either[Throwable, A]] = map(_.right[Throwable]) except (_.left.pure[IO])
def catchSomeLeft[B](p: Throwable => Option[B]): IO[Either[B, A]] = for {
r <- this.catchLeft
x <- r match {
case Right(v) => Right(v).pure[IO]
case Left(e) => p(e) cata (b => Left[B, A](b).pure[IO], throw e)
}
} yield x
def onException[B](action: IO[B]): IO[A] = this except (e => for {
_ <- action
a <- (throw e) : IO[A]
} yield a)
def bracket[B, C](after: A => IO[B])(during: A => IO[C]): IO[C] = for {
a <- this
r <- during(a) onException after(a)
_ <- after(a)
} yield r
def ensuring[B](sequel: IO[B]): IO[A] = for {
r <- onException(sequel)
_ <- sequel
} yield r
def bracket_[B, C](after: IO[B])(during: IO[C]): IO[C] =
bracket(_ => after)(_ => during)
def bracketOnError[B, C](after: A => IO[B])(during: A => IO[C]): IO[C] = for {
a <- this
r <- during(a) onException after(a)
} yield r
}
class IORef[A](val value: STRef[RealWorld, A]) extends NewType[STRef[RealWorld, A]] {
def read: IO[A] = stToIO(value.read)
def write(a: => A): IO[Unit] = stToIO(value.write(a) map (_ => ()))
def mod(f: A => A): IO[A] = stToIO(value.mod(f) >>= (_.read))
}
object IO {
def apply[A](f: World[RealWorld] => Trampoline[(World[RealWorld], A)]): IO[A] = new IO[A] {
private[effects] def apply(rw: World[RealWorld]) = f(rw)
}
implicit val ioPure: Pure[IO] = new Pure[IO] {
def pure[A](a: => A) = IO(rw => return_((rw, a)))
}
implicit val ioBind: Bind[IO] = new Bind[IO] {
def bind[A, B](io: IO[A], f: A => IO[B]): IO[B] = io flatMap f
}
implicit val ioFunctor: Functor[IO] = new Functor[IO] {
def fmap[A, B](io: IO[A], f: A => B): IO[B] = io map f
}
}