【问题标题】:Scala polymorphism - covariant and type boundScala 多态性 - 协变和类型绑定
【发布时间】:2017-12-19 10:39:02
【问题描述】:

使用 Scala...我不知道如何以混合类型绑定和协变的方式使用多态性。

简而言之,我认为我需要类似这种类型签名的东西...但是如果您按照我的虚拟示例进行操作,您就会明白我为什么会出现在这里...也许我错了。

def func[+T <: U](func: Seq[T] => T)(iter: Iterator[String]): Map[String, String] = ???

但是这种方法会产生...

>> error: ']' expected but identifier found

这是一个虚拟示例,演示了我正在尝试做的事情...我可以通过仅使用基本特征 Record 来回避这个问题...但我想让它与多态性一起工作实际代码中的其他原因。

设置

// underlying trait to hold key and value
trait Record {
  def k: String 
  def v: String
  def isDefined: Boolean
}

// companion object with apply method
object Record {
  def apply(s: String): Record = s.split(",") match {
    case Array(k,v) => new ValidRecord(k,v).asInstanceOf[Record]
    case _          => EmptyRecord.asInstanceOf[Record]
  }
}

// singleton for empty records
object EmptyRecord extends Record {
  val k = ""
  val v = ""
  val isDefined = false
}

// class for actual data
class ValidRecord(val k: String, val v: String) extends Record {
  val isDefined = true
}

多态函数

注意 - 从 Iterator 到 Seq 这里看起来有问题...我正在从 src/main/resources 中读取一个文件...它以 Iterator 的形式出现...我最终需要将它放入映射,所以 .toSeq 和 .groupBy 似乎是合乎逻辑的步骤......它可能只有 100MB 和一百万左右的记录,所以这很好用......但如果有更聪明的方式来从头到尾,我很开放对那个批评也是如此。

def iter_2_map[T <: Record](func: Seq[T] => T)(iter: Iterator[String]): Map[String, String] = {
  iter                               // iterator of raw data
  .map(Record.apply)                 // Iterator[Record]
  .toSeq                             // gives .groupBy() method
  .groupBy(_.k)                      // Map[k -> Seq[Record]]; one Seq of records per k
  .mapValues(func) // <<< ERROR HERE //function to reduce Seq[Record] to 1 Record
  .filter(_._2.isDefined)            // get rid of empty results
  .mapValues(_.v)                    // target of Map is just v
}

错误

found   : Seq[T] => T
required: Seq[Record] => ?
          .mapValues(func)
                     ^

如果我分解所有这些步骤并在每个相关步骤中声明类型...错误将变为此...

found   : Seq[T] => T
required: Seq[Record] => Record
          .mapValues(func)
                     ^

所以这就是我被难住的地方。我认为使 T 协变解决了这个问题... T 是 Record 的声明子类型,但也许它没有将 Seq[T] 识别为 <: seq>

但是进行此更改会在顶部产生错误...

def iter_2_map[+T <% Record](func: Seq[T] => T)(iter: Iterator[String]): Map[String, String] = {
  ???
}

回到这个...

>> error: ']' expected but identifier found

我是否走在正确的轨道上?

【问题讨论】:

  • 不清楚你的trait Record是否应该是sealed。换句话说,EmptyRecordValidRecord 是唯一预期的子类吗?或者您想创建具有更多字段和其他解析逻辑的更具体的类型?所以当你写T &lt;: Record时,不清楚你在想什么T。一方面,您显式调用Record.apply,因此除了这两个之外您无法获得任何其他内容。另一方面,如果您使用Record.apply,您可以同时获得它们,因此您不能将类型缩小到比只是Record。总结一下:告诉我们你将如何使用你的iter_2_map

标签: scala polymorphism covariance type-bounds


【解决方案1】:

您错误地使用了+。它仅与 classes 的类型参数一起使用,以表明该类的参数应该是协变的。 将它与方法一起使用没有多大意义(Seq[T] 实际上 Seq[Record] 的子类 - 因为 Seq 是协变的,但这对您没有帮助,因为 函数 在其参数类型中是逆变,因此Function[Seq[T], T]Function[Seq[Record], T]超类,而不是子类)。原因如下:

.groupBy(_.k) 之后是Map[String, Seq[Record]]。 现在,您正在对它执行.mapValues(func),并尝试将一个函数传递给它,该函数需要一个Seq[T]。这行不通。

想象一下,RecordAnimalTDog ... 而 funcmakeBark ... 现在你正试图将一堆动物传递给它,其中一些是Cats,一些是Birds,还有一些可能是Fish。你不能让它们都叫,对吗?

你可以声明你的reducer函数接受Record序列而不是T

   def iter_2_map[T <: Record](func: Seq[Record] => T)(iter: Iterator[String])

这将编译,但它似乎对你来说不是很有用,因为你似乎 期望你的 func 能够返回 both EmptyRecordValidRecord,而不仅仅是 T(因为之后您要过滤掉空的内容)。因此,实际上您似乎根本不需要类型参数:

  def iter_2_map(func: Seq[Record] => Record)(iter: Iterator[String])

【讨论】:

    猜你喜欢
    • 2013-11-29
    • 1970-01-01
    • 1970-01-01
    • 2012-04-08
    • 2013-12-23
    • 1970-01-01
    • 2020-01-25
    • 2018-08-14
    • 1970-01-01
    相关资源
    最近更新 更多