不同类型任务的功能组合-Scala
目前,我正在设计在Scala中构建通用管道(纯粹是为了学习)。为此,我从一个基本构造开始,Task接受一些TaskConfiguration(现在,我们可以假设这个TaskConfiguration是一个特定于任务功能的case类)。性状结构如下:不同类型任务的功能组合-Scala,scala,function-composition,Scala,Function Composition,目前,我正在设计在Scala中构建通用管道(纯粹是为了学习)。为此,我从一个基本构造开始,Task接受一些TaskConfiguration(现在,我们可以假设这个TaskConfiguration是一个特定于任务功能的case类)。性状结构如下: trait Task[T <: TaskConfiguration] { type Out def taskConfiguration: T def execute(previousOutput: Option[Out]): Op
trait Task[T <: TaskConfiguration] {
type Out
def taskConfiguration: T
def execute(previousOutput: Option[Out]): Option[Out]
}
trait Task[T您可以使用与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")))
导入scala.util.{Try,Success,Failure}
类型TaskConfiguration=Any
特质任务[-C选项[O]){
def execute(c:c):选项[O]=f.apply(c)
}
案例类任务链[C,O尾部匹配{
案例头::Nil=>head.execute(o)
case head::tail=>runTasks(head.execute(o),tail)
case Nil=>???//这不应该发生!
}
案例无=>无
}
}
运行任务(一些(初始),任务)
}
}
//示例如下:
valt1:Task[Int,Int]=Task(i=>Some(i*2))
valt2:Task[Int,Int]=Task(i=>Some(i-100))
val t3:Task[Int,Int]=Task(i=>if(i>0)Some(i)else None)
val链:任务链[Int,Int]=任务链(列表(t1,t2,t3))
println(链运行(100))
println(链运行(10))
引述:
您需要了解的是,如果您将任务
s打包到列表[任务]
并将其用作任务链
s,输出必须至少是输入的一个子类型。C我建议看看能为您提供什么。按照这种方法,我将开始定义用于定义管道程序的ADT。类似于:
trait TaskE[Effect]
case class ReadTask[Input, SourceConfig](source: SourceConfig) extends TaskE[Input]
case class WriteTask[Output, SinkConfig](out: Output, sink: SinkConfig) extends TaskE[Unit]
然后应用免费monad(如上链接中所述)来定义管道流。类似于:
val pipeline: Task[Unit] =
for {
input1 <- read(source1)
input2 <- read(source2)
_ <- write(input1 + input2, sink1)
} yield ()
您可以拥有任意数量的“编译器”,但这并不意味着更改管道(“程序”)定义。WDYM通过“编写要执行的方法调用”?@YuvalItzchakov这里我们处理的是列表[任务]并且需要对每个任务依次调用execute方法。这就像Task1的输出应该输入到Task2,依此类推。例如,如果管道类似于复制一个文件,那么相应的任务将是ReadTask,WriteTask。ReadTask将从文件中读取数据并将这些行提供给WriteTask,而后者又将写入另一个文件。No No No No modHFGen.dll如果您需要更多详细信息,请告诉我。感谢您的快速响应。看来,我的要求有点不同。“O”不是TaskConfiguration的子类型。它应该是类型成员(需要由Trait的子类重写),表示“execute”方法的输出。“execute”采用前面的方法“Task's execute method's output”作为输入。请参考我对Yuval问题的评论。如果出现错误,请纠正我。@Krishna听着,您需要了解的是,如果您将任务
打包到列表[任务]
并将其作为一个任务链使用,输出必须至少是输入的一个子类型。C@Krishna看一看这个例子:根据上面的评论更改了我的特征,效果很好。非常感谢。@Krishna很乐意帮助!@Santos free monads似乎是一个有趣的概念。但是,你的回答不是addressing the composition part.在给定的上下文中,我们将有一个需要组合的列表[Task],这与管道示例不同,在管道示例中,我事先知道存在多少任务及其执行顺序。
val pipeline: Task[Unit] =
for {
input1 <- read(source1)
input2 <- read(source2)
_ <- write(input1 + input2, sink1)
} yield ()
val myCompiler: Task ~> Id = ???
val tryCompiler: Task ~> Try = ???
pipeline.foldMap(myCompiler) // Id[Unit]
pipeline.foldMap(tryCompiler) // Try[Unit]