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].get
Must 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
This Will Require a Major Version Change!
But It's Probably Worth It!
We can remove the
Uparameter by including a type constructorAudinside of theAuditortrait. If we retainNandB, the output of the auditor is still well typed. If we have theMorphableAuditorpassed 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 likeDefaultValueAuditors(see below in the code). The nice thing is that the Scala-specific features (i.e., type constructors,Auxpattern) are only used by Scala code. Therefore, we can instantiate the stuff from Java just fine in a type-safe way. If we makechangeTypepackage private toaloha, 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 ...
Option Auditor
Tree Auditor
Covariance of
Ais required for this to work.Default Value Auditor
Provides a default value that is injected on failure.
Calling Code
Must use original auditor, not one from changeType for auditing the top-level model output