Created
August 15, 2016 22:32
-
-
Save sir-wabbit/c3bfa01302059a5306c8af47b3d24128 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cats.{MonadError, Monad} | |
import cats.data.Xor | |
import scala.annotation.tailrec | |
object io { | |
object unsafe { | |
type Val = Any with ({ type Tag = Any }) | |
object Val { | |
val unit: Val = cast(()) | |
@inline def cast[A](x: A): Val = x.asInstanceOf[Val] | |
@inline def castF[F[_], A](x: F[A]): F[Val] = x.asInstanceOf[F[Val]] | |
@inline def reify[A](x: Val): A = x.asInstanceOf[A] | |
@inline def reifyF[F[_], A](x: F[Val]): F[A] = x.asInstanceOf[F[A]] | |
@inline def castK2[F[_, _], A, B](op: F[A, B]): F[Val, Val] = op.asInstanceOf[F[Val, Val]] | |
} | |
final case class TAQ[F[_, _], A, B](repr: Vector[F[Val, Val]]) { | |
def :+[C](op: F[B, C]): TAQ[F, A, C] = TAQ[F, A, C](repr :+ Val.castK2(op)) | |
def +:[C](op: F[C, A]): TAQ[F, C, B] = TAQ[F, C, B](Val.castK2(op) +: repr) | |
def ++[C](that: TAQ[F, B, C]): TAQ[F, A, C] = TAQ[F, A, C](repr ++ that.repr) | |
} | |
object TAQ { | |
def apply[F[_, _], A, B](fab: F[A, B]): TAQ[F, A, B] = | |
new TAQ[F, A, B](Vector(Val.castK2(fab))) | |
def apply[F[_, _], A, B, C](fab: F[A, B], fbc: F[B, C]): TAQ[F, A, C] = | |
new TAQ[F, A, C](Vector(Val.castK2(fab), Val.castK2(fbc))) | |
} | |
} | |
object alg { | |
sealed abstract class Op[A, B] extends Product with Serializable | |
object Op { | |
final case class Map[A, B](f: A => B) extends Op[A, B] | |
final case class Bind[A, B](f: A => IO[B]) extends Op[A, B] | |
final case class Handle[A](f: Throwable => IO[A]) extends Op[A, A] | |
} | |
import unsafe.TAQ | |
final case class IO[A](ops: TAQ[Op, Unit, A]) { | |
def map[B](f: A => B): IO[B] = IO(ops :+ Op.Map(f)) | |
def flatMap[B](f: A => IO[B]): IO[B] = IO(ops :+ Op.Bind(f)) | |
def handleErrorWith(f: Throwable => IO[A]): IO[A] = IO(ops :+ Op.Handle(f)) | |
def unsafeAttempt(): Throwable Xor A = { | |
val unit = Xor.right(unsafe.Val.unit) | |
@tailrec def go(head: Throwable Xor unsafe.Val, rest: Vector[Op[unsafe.Val, unsafe.Val]]): Throwable Xor unsafe.Val = { | |
(head, rest.headOption) match { | |
case (_, None) => | |
// println("done") | |
head | |
case (Xor.Left(e), Some(Op.Handle(f))) => | |
// println("handle") | |
val cont = Xor.catchNonFatal(f(e).ops.repr) | |
val newHead = cont.fold(_ => head, _ => unit) | |
val newTail = cont.fold(e => rest.tail, c => c ++ rest.tail) | |
go(newHead, newTail) | |
case (Xor.Left(e), _) => | |
// println("exception propagation") | |
go(head, rest.tail) | |
case (Xor.Right(x), Some(Op.Handle(f))) => | |
// println("value propagation") | |
go(head, rest.tail) | |
case (Xor.Right(x), Some(Op.Map(f))) => | |
// println("map") | |
val newHead = Xor.catchNonFatal(f(x)) | |
go(newHead, rest.tail) | |
case (Xor.Right(x), Some(Op.Bind(f))) => | |
// println("bind") | |
val cont = Xor.catchNonFatal(f(x).ops.repr) | |
val newHead = cont.map(_ => unsafe.Val.unit) | |
val newTail = cont.fold(e => rest.tail, c => c ++ rest.tail) | |
go(newHead, newTail) | |
} | |
} | |
go(unit, this.ops.repr).map(unsafe.Val.reify[A]) | |
} | |
def unsafeRun(): A = unsafeAttempt().fold(e => throw e, a => a) | |
} | |
object IO { | |
private[iocat] def apply[A](a: Op[Unit, A]) = new IO(TAQ(a)) | |
private[iocat] def apply[A, B](a: Op[Unit, A], b: Op[A, B]) = new IO(TAQ(a, b)) | |
def pure[A](x: A): IO[A] = IO(Op.Map[Unit, A](_ => x)) | |
def capture[A](x: => A): IO[A] = IO(Op.Map[Unit, A](_ => x)) | |
def raiseError[A](e: Throwable): IO[A] = IO(Op.Map[Unit, A](_ => throw e)) | |
implicit val instance: Monad[IO] with MonadError[IO, Throwable] = new Monad[IO] with MonadError[IO, Throwable] { | |
override def pure[A](x: A): IO[A] = IO.pure(x) | |
override def raiseError[A](e: Throwable): IO[A] = IO.raiseError(e) | |
override def map[A, B](fa: IO[A])(f: A => B): IO[B] = fa.map(f) | |
override def flatMap[A, B](fa: IO[A])(f: A => IO[B]): IO[B] = fa.flatMap(f) | |
override def handleErrorWith[A](fa: IO[A])(f: Throwable => IO[A]): IO[A] = fa.handleErrorWith(f) | |
} | |
} | |
} | |
object std { | |
import alg._ | |
def putStrLn(s: String): IO[Unit] = IO.capture(println(s)) | |
def readLn: IO[String] = IO.capture(scala.io.StdIn.readLine()) | |
} | |
} | |
object Main { | |
import io.alg._ | |
import io.std._ | |
def pureMain(args: Vector[String]): IO[Unit] = for { | |
s <- readLn | |
_ <- putStrLn(s) | |
} yield () | |
def main(args: Array[String]): Unit = | |
pureMain(args.toVector).unsafeRun() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment