所以我设法做到的方式是这样的(尽管它仍然可以使用改进 - 例如,它构造了列表错误和函数错误共有类型的错误序列):
HList.scala
import HList.::
sealed trait HList [T <: HList[T]] {
def ::[H1](h : H1) : HCons[H1, T]
}
object HList {
type ::[H, T <: HList[T]] = HCons[H, T]
val HNil = new HNil{}
}
final case class HCons[H, T <: HList[T]](head: H, tail: T) extends HList[HCons[H, T]] {
override def ::[H1](h: H1) = HCons(h, this)
def apply[F, Out](fun : F)(implicit app : HApply[HCons[H, T], F, Out]) = app.apply(this, fun)
override def toString = head + " :: " + tail.toString
None
}
trait HNil extends HList[HNil] {
override def ::[H1](h: H1) = HCons(h, this)
override def toString = "HNil"
}
HListApplication.scala
@implicitNotFound("Could not find application for list ${L} with function ${F} and output ${Out}.")
trait HApply[L <: HList[L], -F, +Out] {
def apply(l: L, f: F): Out
}
object HApply {
import HList.::
implicit def happlyLast[H, Out] = new HApply[H :: HNil, H => Out, Out] {
def apply(l: H :: HNil, f: H => Out) = f(l.head)
}
implicit def happlyStep[H, T <: HList[T], FT, Out](implicit fct: HApply[T, FT, Out]) = new HApply[H :: T, H => FT, Out] {
def apply(l: H :: T, f: H => FT) = fct(l.tail, f(l.head))
}
}
ErrorProne.scala
sealed trait ErrorProne[+F, +S]
case class Success [+F, +S] (result : S) extends ErrorProne[F, S]
case class Failure [+F, +S] (errors : Seq[F]) extends ErrorProne[F, S]
argList.scala
import HList.::
import HList.HNil
sealed trait ArgList [E, L <: HList[L]] {
def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]])
: ErrorProne[E, S]
def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L]
}
case class SuccessArgList [E, L <: HList[L]] (list : L) extends ArgList[E, L] {
def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]])
: ErrorProne[E, S] = app.apply(list, fun)
override def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L] = argument match {
case Success(a) => SuccessArgList(a :: list)
case Failure(e) => FailureArgList(e)
}
}
case class FailureArgList [E, L <: HList[L]] (errors : Seq[E]) extends ArgList[E, L] {
def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]])
: ErrorProne[E, S] = Failure(errors)
override def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L] = argument match {
case Success(a) => FailureArgList(errors)
case Failure(newErrors) => FailureArgList(Seq[EX]() ++ errors ++ newErrors)
}
}
object Args {
def :: [E1, A] (argument : ErrorProne[E1, A]) : ArgList[E1, A :: HNil] = argument match {
case Success(a) => SuccessArgList(a :: HNil)
case Failure(e) => FailureArgList(e)
}
}
用法
val result = ((parseTemplate("Suc") :: Args).apply(checkConsistency _) ::
(parseTemplate("Suc") :: Args).apply(checkConsistency _) :: Args)
.apply(checkConformance _)
trait Err
case class Err1 extends Err
case class Err2 extends Err
case class Err3 extends Err
def parseTemplate(name : String) : ErrorProne[Err, Int] = if(name == "Suc") Success(11) else Failure(Seq(Err1()))
def checkConsistency(value : Int) : ErrorProne[Err2, Double] = if(value > 10) Success(0.3) else Failure(Seq(Err2(), Err2()))
def checkConformance(left : Double) (right : Double) : ErrorProne[Err3, Boolean] =
if(left == right) Success(true) else Failure(Seq(Err3()))