-
Notifications
You must be signed in to change notification settings - Fork 12
Remove U type parameter from Auditors, Models, Factories #146
Description
This Will Require a Major Version Change!
But It's Probably Worth It!
We can remove the U parameter by including a type constructor Aud inside of the Auditor trait. If we retain N and B, the output of the auditor is still well typed. If we have the MorphableAuditor passed to the factory still used as the auditor for the top-level models produced by the factories, we still get back the nice values we want. We can still do things like DefaultValueAuditors (see below in the code). The nice thing is that the Scala-specific features (i.e., type constructors, Aux pattern) are only used by Scala code. Therefore, we can instantiate the stuff from Java just fine in a type-safe way. If we make changeType package private to aloha, which is fine since it's only to be used by the factory, then we should be fine.
Explore this more, but to get started in the meantime ...
import scala.language.higherKinds
type RefInfo[A] = Manifest[A]
trait Auditor[-N, +B] {
type Aud[_]
def success[C](n: N, sub: Seq[Aud[C]] = Nil): B
def failure[C](sub: Seq[Aud[C]] = Nil): B
}
trait MorphableAuditor[-N, +B] extends Auditor[N, B] {
private[aloha] def changeType[M: RefInfo]: Option[MorphableAuditor.Aux[Aud, M, Aud[M]]]
}
object MorphableAuditor {
type Aux[A[_], N, B <: A[N]] = MorphableAuditor[N, B]{ type Aud[M] = A[M] }
}Option Auditor
case class OptionAuditor[N]() extends MorphableAuditor[N, Option[N]] {
type Aud[M] = Option[M]
private[aloha] def changeType[M: RefInfo] = Option(OptionAuditor[M]())
def success[C](n: N, sub: Seq[Aud[C]] = Nil): Option[N] = Option(n)
def failure[C](sub: Seq[Aud[C]] = Nil): Option[N] = None
}Tree Auditor
Covariance of A is required for this to work.
case class Tree[+A](value: Option[A], children: Seq[Tree[Any]] = Nil)
case class TreeAuditor[N]() extends MorphableAuditor[N, Tree[N]] {
type Aud[M] = Tree[M]
private[aloha] def changeType[M: RefInfo] = Option(TreeAuditor[M]())
def success[C](n: N, sub: Seq[Aud[C]] = Nil): Tree[N] = Tree(Option(n), sub)
def failure[C](sub: Seq[Aud[C]] = Nil): Tree[N] = Tree(None, sub)
}Default Value Auditor
Provides a default value that is injected on failure.
case class DefValAud[N, +B](
auditor: MorphableAuditor[N, B],
default: N
) extends MorphableAuditor[N, B] {
type Aud[M] = auditor.Aud[M]
private[aloha] def changeType[M: RefInfo]: Option[MorphableAuditor.Aux[Aud, M, Aud[M]]] =
auditor.changeType[M]
def success[C](n: N, sub: Seq[Aud[C]] = Nil): B = auditor.success(n, sub)
def failure[C](sub: Seq[Aud[C]] = Nil): B = success(default, sub)
}Calling Code
// Mimic a model
case class Model[N, -A, +B](f: A => N, aud: Auditor[N, B]) extends (A => B){
def apply(a: A) = aud.success(f(a))
}
// Create some auditors
val oad = OptionAuditor[Double]()
val oai = oad.changeType[Int].get
val dad = DefValAud(oad, Double.NaN)
val dai = dad.changeType[Int].get
val tai = TreeAuditor[Int]()
val tad = tai.changeType[Double].get
val tdai = DefValAud(tai, Int.MinValue)
val tdad = tdai.changeType[Double].getMust use original auditor, not one from changeType for auditing the top-level model output
val m = Model((d: Double) => (d * 2).toInt, tdai)
val m1 = Model((i: Int) => i / 2d, dad)
m(2) // : Tree[Int] = Tree(Some(4),List())
m1(3) // : Option[Double] = Some(1.5)
// : Tree[Int] = Tree(Some(1),List(Tree(None,List())))
val x = tdai.success(1, Seq(tdad.failure()))
x.value.get // : Int = 1