Skip to content

Instantly share code, notes, and snippets.

@sir-wabbit
Created August 15, 2016 22:32
Show Gist options
  • Save sir-wabbit/c3bfa01302059a5306c8af47b3d24128 to your computer and use it in GitHub Desktop.
Save sir-wabbit/c3bfa01302059a5306c8af47b3d24128 to your computer and use it in GitHub Desktop.
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