package scalaz
package effects
import Scalaz._
sealed trait IO[A] {
private[effects] def apply(rw: World[RealWorld]): (World[RealWorld], A)
def unsafePerformIO: A = apply(realWorld)._2
def flatMap[B](f: A => IO[B]): IO[B] = IO(rw => {
val (nw, a) = apply(rw)
f(a)(nw)
})
def map[B](f: A => B): IO[B] = IO(rw => {
val (nw, a) = apply(rw)
(nw, f(a))
})
def except(handler: Throwable => IO[A]): IO[A] =
IO(rw => try { this(rw) } 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] => (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 => (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
}
}