scala - 我如何解决这个 Scala 函数参数类型删除错误?

标签 scala pattern-matching type-erasure

我正在创建一个 map-reduce 框架,现在我正在尝试创建一个构建器类来实例化处理管道。此构建器需要保存用户指定的函数列表,以便稍后它可以使用这些函数作为参数来构造管道以实例化 Worker 对象。

我正在使用案例类来创建函数“持有者”,以及“ worker ”。我的问题是,当我继续分析持有者时,如果我对它们进行模式匹配,由于类型删除,函数中的类型信息似乎丢失了。这是一些似乎可以重现我遇到的问题的最小代码。

trait Holders

case class MapHolder[A, B](f: A => B) extends Holders

trait Worker

case class MapWrk[A, B](f: A => B) extends Worker

object MyTypeErasureProblem extends App {

  val myFunc = MapHolder((x: Int) => x + 10)

  def buildWorker(hh: Holders) =
    hh match {
      case MapHolder(f) => MapWrk(f)
    }

  println(buildWorker(myFunc).f(10))
}

编译器错误是
Error:(22, 35) type mismatch;
 found   : Nothing => Any
 required: A => Any
      case MapHolder(f) => MapWrk(f)
                                  ^
Error:(26, 33) type mismatch;
 found   : Int(10)
 required: Nothing
  println(buildWorker(myFunc).f(10))
                                ^

如果我们这样定义 buildWorker 就可以解决这个问题:
def buildWorker[A,B](hh: MapHolder[A,B]) = ...

但我需要处理不同种类的 Holders , 包含
case class ReduceHolder[V](f: (V,V) => V) extends Holders

顺便说一下,它在类似的代码中工作得很好,根本没有错误或警告。其实只有泛型类型A=>B好像有问题,因为函数参数变成了Nothing , 传递具有其他泛型类型的对象可以工作,例如Tuple2[A,B] .

这对我来说似乎是一个与类型删除相关的问题,但我该如何解决呢?我试过ClassTag所有的事情,但它没有奏效。

更多信息
这是 Travis 建议的方法的更新,使用 buildWorker Holder里面的方法.

这在这里按我需要的方式工作:
case class DataHolder[T](f: T) {
  def buildWorker() = DataWrk[T](f)
}

case class DataWrk[T](f: T)

object MyTypeErasureProblem2 extends App {
  val pipeline = List(
    DataHolder[Int](10).buildWorker(),
    DataHolder[String]("abc").buildWorker()
  )
  val result = pipeline collect {
    case DataWrk(f: Int) => "Int data"
    case DataWrk(f: String) => "String data"
  }
  result foreach println
}

输出:
Int data
String data

但是如果我们使用函数类型,它就不再起作用了:
case class MapHolder[T](f: T => T) {
  def buildWorker() = MapWrk[T](f)
}

case class MapWrk[T](f: T => T)

object MyTypeErasureProblem extends App {
  val pipeline = List(
    MapHolder[Int]((x: Int) => x + 10).buildWorker(),
    MapHolder[String]((x: String) => x + "abc").buildWorker()
  )
  val result = pipeline collect {
    case MapWrk(f: (Int => Int)) => "Int endofunction"
    case MapWrk(f: (String => String)) => "String endofunction"
  }
  result foreach println
}

输出:
Int endofunction
Int endofunction

当我们尝试匹配实例化的 Worker 时s,第一个case总是匹配。

最佳答案

您可以为您的 buildWorker 添加一点类型清晰度定义:

  def buildWorker[A, B](hh: Holders) =
    hh match {
      case MapHolder(f: (A => B)) => MapWrk(f)
    }

更新:
这里还有一些制作无类型 buildWorker 方法的方法:
首先使用存在类型,但是 case holder:(MapHolder[A, B] forSome {type A; type B})由于某种原因没有编译,所以一种尝试可能是:
  type MapHolderGeneric = MapHolder[A, B] forSome {type A; type B}

  def buildWorker(hh: Holders):Worker = 
    hh match {
      case holder: MapHolderGeneric => MapWrk(holder.f)
    }

next 是相当等效的:
  case holder: MapHolder[_,_] => MapWrk(holder.f)

都知道Nothing关于类型。所以他们的AB将被推断为 Nothing大多数情况下as Travis pointed这只是一种欺骗编译器的方法。

您的示例代码
   case DataWrk(f: Int) => "Int data"
   case DataWrk(f: String) => "String data"

永远无法工作,因为类型在 DataWrk 中没有具体化实例,所以在运行时它可以被检查。但是我们可以使用 scala-reflect 手动将它们具体化。 :
import scala.reflect.runtime.universe._

trait Holders

case class MapHolder[T](f: T => T)(implicit tag: TypeTag[T]) {
  def buildWorker() = MapWrk[T](f)
}

trait Worker

class MapWrk[T](val f: T => T)(implicit val tag: TypeTag[T]) extends Worker

object MapWrk {

  def apply[T](f: T => T)(implicit tag: TypeTag[T]) = new MapWrk[T](f)

  def unapply(worker: Worker): Option[(_ => _, Type)] = worker match {
    case mapWrk: MapWrk[_] => Some((mapWrk.f, mapWrk.tag.tpe))
    case _ => None
  }
}

object MyTypeErasureProblem extends App {
  val pipeline = List(
    MapHolder[Int]((x: Int) => x + 10).buildWorker(),
    MapHolder[String]((x: String) => x + "abc").buildWorker()
  )

  val result = pipeline collect {
    case MapWrk(f, tpe) if tpe =:= typeOf[Int] => "Int endofunction"
    case MapWrk(f, tpe) if tpe =:= typeOf[String] => "String endofunction"
  }

  result foreach println
}

这是打印出你所期望的

更直接的解决方案是将一些隐式类型发送到 unapply方法,并且只使用类型参数来指定我们在这里匹配的类型,但是 this is not implemented yet ,所以我假设 f里面的图案加工还不能打字。但它可能会在 2.12

关于scala - 我如何解决这个 Scala 函数参数类型删除错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29733405/

相关文章:

java - Java中类型删除的异常(exception)是什么?

c++ - 实现类型删除列表以实现快速写入/读取

list - 如何扩展 Scala 列表以启用不是按显式位置而是按给定谓词/条件的切片

scala - 如何在 Scala 中创建只读类成员?

java - 正则表达式模式与数据和日期匹配

c++ - std::function/bind 就像没有标准 C++ 库的类型删除

scala - val b=a(a 是一个数组)和 val b=a.clone() 有什么区别?

scala - 将 SBT 变量写入资源

F# 匹配表达式和 'as' 模式

clojure - 如何使用 clojure.core.match 匹配层次结构?