Skip to content
Permalink
master
Switch branches/tags
Go to file
1 contributor

Users who have contributed to this file

/*
Mitigation, version 1.0.0. Copyright 2018 Jon Pretty, Propensive Ltd.
The primary distribution site is: https://propensive.com/
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
*/
package mitigation
import scala.collection.generic.CanBuildFrom
import language.higherKinds
import language.implicitConversions
import totalitarian._, ident.Disjunct
import scala.reflect.ClassTag
import scala.util._, control.NonFatal
import scala.annotation.unchecked.uncheckedVariance
object `package` {
implicit class Unitise[T](value: T) { def unit(): Unit = () }
implicit def mitigate[T, E <: Base](block: => T): Mitigable[T, E] =
new Mitigable(() => block)
type ![T <: Throwable] = totalitarian.![T]
type |[A, B <: Throwable] = A with ![B]
type ~ = Base
def on[T](implicit key: ident.Key[T, _]): OfType[T, key.Value, ident.type] =
totalitarian.ident.on[T]
def otherwise(implicit key: ident.Key[Surprise[_], _]): OfType[Surprise[_], key.Value, ident.type] =
totalitarian.ident.on[Surprise[_]]
implicit class Sequenceable[Coll[A] <: Traversable[A], T, E <: Base]
(coll: Coll[Result[T, E]]) {
def sequence(implicit cbf: CanBuildFrom[Nothing, T, Coll[T]]) = Result.sequence(coll)
}
implicit class Ascription[T]
(option: Option[T]) {
def ascribe[E <: Throwable: TypeId](exception: => E): Result[T, ~ | E] =
Result.ascribe[T, E](exception)(option)
}
}
object Mitigable {
class Apply[E <: Base](val nothing: Int) extends AnyVal {
def apply[T](block: => T): Mitigable[T, E] = new Mitigable(() => block)
}
def apply[E <: Base]: Apply[E] = new Apply(0)
}
class Mitigable[T, E <: Base](val fn: () => T) extends AnyVal {
def unary_~[TConst[_, _ <: Base]](implicit mitigation: Mitigation[TConst]): TConst[T, E] =
mitigation(fn())
}
object Mitigation {
type Drop[Tc[_]] = ({ type Two[T, E <: Base] = Tc[T] })
implicit val options: Mitigation[Drop[Option]#Two] =
new Mitigation[Drop[Option]#Two] {
def apply[T, E <: Base](fn: => T): Option[T] =
try Some(fn) catch { case NonFatal(_) => None }
}
implicit val tries: Mitigation[Drop[Try]#Two] =
new Mitigation[Drop[Try]#Two] {
def apply[T, E <: Base](fn: => T): Try[T] = Try(fn)
}
implicit val results: Mitigation[Result] =
new Mitigation[Result] {
def apply[T, E <: Base](fn: => T): Result[T, E] = Result(fn)
}
}
trait Mitigation[TConst[_, _ <: Base]] { def apply[T, E <: Base](fn: => T): TConst[T, E] }
object Result extends Result_1 {
def apply[T, E <: Base](fn: => T): Result[T, E] = expect(Answer(fn))
def ascribe[T, E <: Throwable: TypeId](exception: => E)(option: Option[T]): Result[T, ~ | E] =
option match {
case None => Aborted(Disjunct(exception))
case Some(value) => Answer(value)
}
def sequence[T, E <: Base, Coll[A] <: Traversable[A]]
(xs: Coll[Result[T, E]])
(implicit cbf: CanBuildFrom[Nothing, T, Coll[T]])
: Result[Coll[T], E] = {
val builder = cbf()
(xs.foldLeft(Answer(()): Result[Unit, E]) { case (acc, next) =>
acc.flatMap { _ => next.map { e => builder += e } }
}).map { _ => builder.result }
}
def expect[T, E <: Base](result: => Result[T, E]): Result[T, E] =
try result catch { case NonFatal(exception) => Surprise(exception) }
def rescue[From <: Exception]: Rescue[From] = new Rescue[From](0)
//implicit def typeError[T1, T, F <: Base, D <: Base](from: Result[T1, F]): Result[T, D] =
// macro Macros.typeError[T1, T, F, D]
def answer[T](value: T): Result[T, ~] = Answer(value)
def abort[T, E <: Throwable: TypeId](exception: E): Result[T, Base with ![E]] = Aborted(Disjunct(exception))
def erratum[T, E <: Throwable: TypeId](value: T, exception: E): Result[T, ~ | E] = Errata(value, Disjunct(exception))
def surprise[T](exception: Throwable) = Surprise[T](exception)
}
trait Result_1 {
//implicit def generalTypeError[T, F <: Base, R](from: Result[T, F]): R =
// macro Macros.generalTypeError[T, F, R]
//implicit def generalTypeError2[T, R <: Base, F](from: F): Result[T, R] =
// macro Macros.generalTypeError2[T, R, F]
}
class Rescue[From <: Exception](val nothing: Int) extends AnyVal {
def apply[To <: Exception: TypeId, T](fn: From => To)(block: => T)(implicit ev: ClassTag[From]): Result[T, ![To]] = try Answer(block) catch {
case e: From => Aborted(Disjunct(fn(e)))
case NonFatal(e) => Surprise(e)
}
def apply[To <: Exception: TypeId, T](exception: => To)(block: => T)(implicit ev: ClassTag[From]): Result[T, ![To]] =
apply[To, T] { (e: From) => exception } (block)
}
case class Filtered() extends Exception
sealed trait Result[+Type, -E <: Base] {
final def map[S](fn: Type => S): Result[S, E] = this match {
case Answer(value) =>
Result.expect(Answer(fn(value)))
case Errata(value, initial, ensuing @ _*) =>
Result.expect(Errata(fn(value), initial, ensuing: _*))
case Aborted(initial, ensuing @ _*) =>
Aborted(initial, ensuing: _*)
case Surprise(exception) =>
Surprise(exception)
}
final def foreach(fn: Type => Unit): Unit = {
map[Unit](fn(_))
()
}
final def filter(fn: Type => Boolean): Result[Type, E with ![Filtered]] = this match {
case Answer(value) =>
if(fn(value)) Answer(value) else Aborted(Disjunct(Filtered()))
case Errata(value, initial, ensuing @ _*) =>
Aborted(initial, (ensuing :+ Disjunct(Filtered())): _*)
case other => other
}
def withFilter(fn: Type => Boolean): Result[Type, E with ![Filtered]] = filter(fn)
def escalate[E2 <: Exception: TypeId](exception: => E2): Result[Type, E with ![E2]] = this match {
case Errata(value, initial, ensuing @ _*) => Aborted(Disjunct(exception), (initial +: ensuing): _*)
case Answer(value) => Answer(value)
case Surprise(surprise) => Surprise(surprise)
case Aborted(terminal, preceding @ _*) => Aborted(terminal, preceding: _*)
}
final def flatMap[S, E2 <: Base](fn: Type => Result[S, E2]): Result[S, E with E2] = this match {
case Answer(value) => Result.expect(fn(value))
case Errata(value, initial, ensuing @ _*) =>
Result.expect(fn(value)) match {
case Answer(value) => Errata(value, initial, ensuing: _*)
case Aborted(terminal, preceding @ _*) => Aborted(terminal, preceding: _*)
case Surprise(exception) => Surprise(exception)
case Errata(value, initial2, ensuing2 @ _*) =>
Errata(value, initial, initial2 +: (ensuing2 ++ ensuing): _*)
}
case Aborted(terminal, preceding @ _*) => Aborted(terminal, preceding: _*)
case Surprise(exception) => Surprise(exception)
}
def opt: Option[Type]
def unsafeGet: Type = opt.get
def successful: Boolean
def errata: List[Disjunct[E]]
def exceptions: List[Throwable] = errata.map(_.value match { case e: Throwable => e })
// replaces any sort of failure
def remedy[T >: Type](value: => T): Result[T, Base] = this match {
case Answer(value) => Answer(value)
case Errata(oldValue, initial, ensuing @ _*) => Answer(value)
case Surprise(error) => Surprise(error)
case Aborted(terminal, preceding @ _*) => Answer(value)
}
def downplay[T >: Type](value: => T): Result[T, E] = this match {
case Answer(value) => Answer(value)
case Errata(value, initial, ensuing @ _*) => Errata(value, initial, ensuing: _*)
case Surprise(error) => Surprise(error)
case Aborted(terminal, preceding @ _*) => Errata(value, terminal, preceding: _*)
}
// Disregards errata, but not aborted
def abide[T >: Type](value: T): Result[T, E] = this match {
case Answer(value) => Answer(value)
case Errata(value, initial, ensuing @ _*) => Answer(value)
case Surprise(error) => Surprise(error)
case Aborted(terminal, preceding @ _*) => Aborted(terminal, preceding :_*)
}
def recover[T <: Throwable, T2 <: ![T], V, W](cases: MapClause[T, T2, Type @uncheckedVariance, Type, V, W, ident.type]*)(implicit ev: Disjunct.AllCases[T2, E]): Type = this match {
case Answer(value) => value
case Aborted(terminal, preceding @ _*) =>
val thisCase = cases.find(_.fromTypeTag == terminal.typeId).get
thisCase.action(terminal.value.asInstanceOf[V])
case Errata(value, initial, ensuing @ _*) =>
val thisCase = cases.find(_.fromTypeTag == initial.typeId).get
thisCase.action(initial.value.asInstanceOf[V])
case Surprise(exception) =>
val thisCase = cases.find(_.fromTypeTag == implicitly[TypeId[Surprise[_]]]).get
thisCase.action(Surprise(exception).asInstanceOf[V])
}
def consolidate[T <: Throwable, T2 <: ![T], R, R2, V, W](cases: MapClause[T, T2, R, R2, V, W, ident.type]*)(implicit ev: Disjunct.AllCases[T2, E]): Result[Type, Base with W] = this match {
case Answer(value) => Answer(value)
case Aborted(terminal, preceding @ _*) =>
Aborted(terminal.map(cases: _*), preceding.map { p => p.map(cases: _*) }: _*)
case Errata(value, initial, ensuing @ _*) =>
Errata(value, initial.map(cases: _*), ensuing.map { p => p.map(cases: _*) }: _*)
case Surprise(exception) =>
Surprise(exception)
}
def repair[T <: Throwable, T2 <: ![T], V, W <: Base](cases: MapClause[T, T2, Result[Type @uncheckedVariance, W], Result[Type, W], V, W, ident.type]*)(implicit ev: Disjunct.AllCases[T2, E]): Result[Type, Base with W] = this match {
case Answer(value) => Answer(value)
case Surprise(exception) => Surprise(exception)
case Aborted(terminal, preceding @ _*) =>
terminal.map(cases: _*)
case Errata(value, initial, ensuing @ _*) =>
initial.map(cases: _*)
}
}
case class Answer[Type](value: Type) extends Result[Type, Base] {
def opt: Option[Type] = Some(value)
def successful: Boolean = true
def errata: List[Disjunct[Base]] = Nil
}
case class Surprise[Type](error: Throwable) extends Throwable with Result[Type, Base] {
def opt: Option[Type] = None
def successful: Boolean = false
override def exceptions: List[Throwable] = List(error)
def errata: List[Disjunct[Base]] = Nil
}
case class Aborted[Type, E <: Base](terminal: Disjunct[E], preceding: Disjunct[E]*) extends Result[Type, E] {
def successful: Boolean = false
def opt: Option[Type] = None
def errata: List[Disjunct[E]] = preceding.to[List] :+ terminal
}
case class Errata[Type, E <: Base](
value: Type,
initial: Disjunct[E],
ensuing: Disjunct[E]*
)
extends Result[Type, E] {
def opt: Option[Type] = None
def errata: List[Disjunct[E]] = initial +: ensuing.to[List]
def successful: Boolean = false
}