scala

时间:2015-05-30 18:21:29

标签: scala

我不确定标题是最好的描述我的问题,但让我们试一试。

我有一个后台作业执行应用程序,类似于简单的管道处理。有Command个对象进行一些计算并返回OUTPUTWorker作为输入接收OUTPUT并且可以返回Result 对象模型看起来像这样:

type OUTPUT <: AnyRef
trait Command[OUTPUT] {
  def doSomething(): OUTPUT
}
sealed trait Worker[IN <: AnyRef, OUT <: Result] {
  def work(input: IN): OUT
}

case class WorkA() extends Worker[String, LongResult] {
  override def work(input: String): LongResult = LongResult(Long.MaxValue)
}

case class WorkB() extends Worker[Long, StringResult] {
  override def work(input: Long): StringResult = StringResult(input.toString)
}

这种方法几乎没有问题:

Worker集合上进行映射时,我无法确保工作人员接受与输入相同的OUTPUT

除非我正在映射Command的列表,否则代码因为类型擦除而无法编译 - 它需要_$1但收到LongString (之前被接受为OUTPUT的所有内容)

val workers = List(
  new WorkA(),
  new WorkB()
)

val aSimpleCommand = new Command[Long] {
  override def doSomething() = 123123123L
}
// Obviously this doesn't compile.
workers.map(worker => worker.work(aSimpleCommand.doSomething()))

我正在寻找合适的Scala机制来在编译时禁止它。我如何仅映射实际支持OUTPUT的工人 - 在这种情况下,仅WorkB

2 个答案:

答案 0 :(得分:1)

如果你想在编译时这样做,你可以使用无形的HLists,一直保留列表的类型,然后使用Poly来处理这些情况:

val myWorkers: WorkA :: WorkB :: WorkB :: HNil =
  WorkA() :: WorkB() :: WorkB() :: HNil

object doWork extends Poly1 {
  implicit def caseLong[A] = at[Worker[Long, A]] {
    w => w.work(aSimpleCommand.doSomething())}
  implicit def caseString = //whatever you want to do with a String worker
}

myWorkers map doWork

对于一个不太安全的例子,只要你有具体的类型,就可以匹配案例:

val myWorkers: List[Worker[_, _]] = ...
myWorkers collect {
  case wa: WorkA => //works
  case lw: Worker[Long, _] => //doesn't work
}

答案 1 :(得分:0)

如果明确说明扩展Worker[Long, _]的所有类是可行的,那么你可以将工作者映射到部分函数上,如下所示:

val res = workers map {
  case b: WorkB => Some(b.work(aSimpleCommand.doSomething()))
  case _ => None
}

在您的示例中,这将返回List(None, Some(StringResult(123123123))。您还可以只收集现有值:

res collect {case Some(r) => r} // List(StringResult(123123123))

现在,这不是一个非常实用的解决方案。也许以下想法可以帮助您找到更好的东西:

正如您已经说过的,由于类型擦除,我们无法创建部分函数来在运行时接受类型Worker[Long, _]的值。 ClassTag(和TypeTag)通过使编译器在运行时为可擦除类型创建可访问的证据来提供此问题的解决方案。例如,以下函数提取工作者输入的运行时类:

import scala.reflect.ClassTag
def getRuntimeClassOfInput[T: ClassTag](worker: Worker[T, _]) = implicitly[ClassTag[T]].runtimeClass

使用示例:

println(getRuntimeClassOfInput(new WorkB)) // long

问题是,一旦工人进入清单,这似乎不起作用。我想这是因为一旦你在列表中有多个不同的工作人员,列表就变成了List[Worker[Any, Result],你就会丢失所有的类型信息。 可能解决此问题的是Heterogenous Lists,因为与标准列表不同,它们保留所有元素的静态类型信息。