Вы можете использовать шаблон, аналогичный andThen
из функций Scala.
Я собрал небольшой пример:
import scala.util.{Try, Success, Failure}
type TaskConfiguration = Any
trait Task[-C <: TaskConfiguration, +O <: TaskConfiguration] {
def execute(configuration: C): Option[O]
def andThen[O2 <: TaskConfiguration](secondTask: Task[O, O2]): Task[C, O2] = {
val firstTask = this
new Task[C, O2] {
def execute(configuration: C): Option[O2] =
firstTask.execute(configuration).flatMap(secondTask.execute(_))
}
}
}
// From here on it's the example!
case class UnparsedNumber(value: String)
trait ParsedNumber {
val value: Int
}
case class ParsedPositiveNumber(int: Int) extends ParsedNumber {
val value: Int = int
}
case class HumanReadableNumber(value: String)
val task1 = new Task[UnparsedNumber, ParsedPositiveNumber] {
def execute(configuration: UnparsedNumber): Option[ParsedPositiveNumber] = {
Try(configuration.value.toInt) match {
case Success(i) if i >= 0 => Some(ParsedPositiveNumber(i))
case Success(_) => None
case Failure(_) => None
}
}
}
val task2 = new Task[ParsedNumber, HumanReadableNumber] {
def execute(configuration: ParsedNumber): Option[HumanReadableNumber] = {
if(configuration.value < 1000 && configuration.value > -1000)
Some(HumanReadableNumber(s"The number is $configuration"))
else
None
}
}
val combined = task1.andThen(task2)
println(combined.execute(UnparsedNumber("12")))
println(combined.execute(UnparsedNumber("12x")))
println(combined.execute(UnparsedNumber("-12")))
println(combined.execute(UnparsedNumber("10000")))
println(combined.execute(UnparsedNumber("-10000")))
Попробуйте!
Изменить:
Что касается ваших комментариев, этот подход может быть больше, чем вы ищете:
case class Task[-C, +O](f: C => Option[O]) {
def execute(c: C): Option[O] = f.apply(c)
}
case class TaskChain[C, O <: C](tasks: List[Task[C, O]]) {
def run(initial: C): Option[O] = {
def runTasks(output: Option[C], tail: List[Task[C, O]]): Option[O] = {
output match {
case Some(o) => tail match {
case head :: Nil => head.execute(o)
case head :: tail => runTasks(head.execute(o), tail)
case Nil => ??? // This should never happen!
}
case None => None
}
}
runTasks(Some(initial), tasks)
}
}
// Example below:
val t1: Task[Int, Int] = Task(i => Some(i * 2))
val t2: Task[Int, Int] = Task(i => Some(i - 100))
val t3: Task[Int, Int] = Task(i => if(i > 0) Some(i) else None)
val chain: TaskChain[Int, Int] = TaskChain(List(t1, t2, t3))
println(chain.run(100))
println(chain.run(10))
Попробуйте!
Цитата:
Что вам нужно понять, так это то, что если вы упакуете Task
s в List[Task]
и используете его как цепочку Task
s, выходные данные должны быть как минимум подтипом ввода. C <: TaskConfiguration
и O <: C
приводит к: O <: C <: TaskConfiguration
, что также означает O <: TaskConfiguration
.
Если вы ничего не поняли, я с удовольствием объясню.
Надеюсь, это поможет.