Skip to content

Instantly share code, notes, and snippets.

@jedws
Last active August 29, 2015 13:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jedws/9086375 to your computer and use it in GitHub Desktop.
Save jedws/9086375 to your computer and use it in GitHub Desktop.
An Actor equivalent of SafeApp
package stuff
import akka.actor.{ Actor, actorRef2Scala }
import kadai.log.Logging
import spray.http.{ HttpFailure, HttpResponse }
import spray.http.HttpEntity.apply
trait IOActor extends Actor {
log: Logging =>
def receiveIO: PartialFunction[Any, IOResult[Unit]]
val handle = (err: Invalid) => log.error(err)
override final def receive = {
case any if (receiveIO.isDefinedAt(any)) =>
receiveIO(any).run.unsafePerformIO.swap.foreach { handle }
case unknown => log.error(unknown)
}
case class Handle[A](io: IOResult[A]) {
def onFail(f: Invalid => (HttpFailure, String)): IOResult[A] =
io.bimap(
err => {
val (status, response) = f(err)
sender ! HttpResponse(status, response)
s"Failed to process store request: HTTP[$status.intValue}] => [$response]".invalid
},
identity
)
}
}
import scalaz._
import syntax.applicative._
import syntax.id._
import Scalaz.Id
import effect._
import util.control.NonFatal
package object stuff {
type Invalid = kadai.Invalid
val Invalid = kadai.Invalid
/**
* The Result monad transformer: essentially specializing Scalaz's EitherT to have
* the left type be an Invalid
*/
type ResultT[F[+_], +A] = EitherT[F, Invalid, A]
object ResultT {
def apply[F[+_], A](a: F[Invalid \/ A]): ResultT[F, A] =
EitherT.eitherT(a)
/** Construct a left disjunction value. */
def left[F[+_], A](a: F[Invalid])(implicit F: Functor[F]): ResultT[F, A] =
EitherT.left(a)
/** Construct a right disjunction value. */
def right[F[+_], A](b: F[A])(implicit F: Functor[F]): ResultT[F, A] =
EitherT.right(b)
}
/** The "basic" version of ResultT. There are no other monads involved except Id */
type Result[+A] = ResultT[Id, A]
object Result extends EitherTInstances with EitherTFunctions {
def fromOption[A](b: Option[A]): Result[A] =
b.fold[Result[A]]("Empty Option".invalidResult)(x => Result(\/.right(x)))
def right[A](a: => A): Result[A] =
ResultT.right(a.point[Id])
def left[A](i: Invalid): Result[A] =
ResultT.left(i.point[Id])
def apply[A](a: Invalid \/ A): Result[A] =
ResultT(a.point[Id])
}
/** Evaluate the given value, which might throw an exception. */
def catchingToResult[A](a: => A): Result[A] =
try ResultT.right(a.point[Id]) // need to do this explicitly due to thunk expansion, otherwise weird error in scalac about implicit conversions
catch {
case NonFatal(e) => e.invalidResult
}
implicit val EachResult =
new Each[Result] {
def each[A](fa: Result[A])(f: A => Unit) = fa foreach f
}
implicit def resultMonoid[A : Monoid] = new Monoid[Result[A]] {
def zero: Result[A] =
Result.right(Monoid[A].zero)
def append(r0: Result[A], r1: => Result[A]): Result[A] =
for {
a0 <- r0
a1 <- r1
} yield implicitly[Monoid[A]].append(a0, a1)
}
/** A Result wrapped in IO, using the ResultT transformer **/
type IOResult[+A] = ResultT[IO, A]
object IOResult extends EitherTInstances with EitherTFunctions {
def apply[A](a: => Result[A]): IOResult[A] =
ResultT {
IO {
{
try a
catch { case NonFatal(e) => e.invalidResult[A] }
}.run
}
}
def right[A](a: => A): IOResult[A] =
ResultT.right { IO { a } }
def left[A](a: => Invalid): IOResult[A] =
ResultT.left { IO { a } }
def mustExist[A](a: Option[A], missing: => String): IOResult[A] =
IOResult {
a.fold[Result[A]](missing.invalidResult)(Result.right(_))
}
}
def catchingIO[A](a: => A): IOResult[A] =
IOResult { catchingToResult { a } }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment