简体   繁体   English

当泛型类型在相同的泛型类型上运行时,Scala类型不匹配

[英]Scala Type mismatch when a generic type operates on the same generic type

I have an generic case class Route that takes in a List of subclasses of Location. 我有一个泛型案例类Route,它接受Location的子类列表。 However in the following method I get a type mismatch in the call to distance expected: head.T, actual: T 但是在下面的方法中,我在调用expected: head.T, actual: T distance遇到类型不匹配expected: head.T, actual: T

case class Route[T <: Location](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, head.distance(h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}

The basic abstract Location class is as follows 基本的抽象Location类如下

abstract class Location(val name: String) {

type T <: Location

def distance(that: T): Double
}

As head and h both come from the same list route I can't understand why these are not the same type. 由于head和h都来自同一个列表route我无法理解为什么这些不是同一类型。

It looks as if F-bounded polymorphism is what you want in this case: 在这种情况下,看起来F-bounded多态性就像你想要的那样:

abstract class Location[L <: Location[L]](val name: String) {
  def distance(that: L): Double
}

case class Route[T <: Location[T]](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, head.distance(h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}

However, you might also consider using a Metric -typeclass instead: 但是,您也可以考虑使用Metric -typeclass:

trait Metric[L] {
  def dist(a: L, b: L): Double
}

case class Route[T: Metric](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, implicitly[Metric[T]].dist(head, h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}

The latter solution would be applicable to more types, for example to (Double, Double) , even if they don't inherit from Location . 后一种解决方案适用于更多类型,例如(Double, Double) ,即使它们不从Location继承。

Here is the typeclass solution again, but with slightly more polished Cats-style syntax that avoids implicitly : 这里是类型类解决方案,但稍微更加优雅的Cats风格语法可以implicitly避免:

trait Metric[L] {
  def dist(a: L, b: L): Double
}

object Metric {
  def apply[T](implicit m: Metric[T]): Metric[T] = m
}

case class Route[T: Metric](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, Metric[T].dist(head, h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}

There is not need for you to define a type T inside your Location abstract class. 您无需在Location抽象类中定义类型T You should proceed as follows: 你应该按如下方式进行:

abstract class Location[T <: Location[T]](val name: String) {
  def distance(that: T): Double
}

case class Route[T <: Location[T]](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, head.distance(h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}

There is no way for the scala compiler to know that type T <: Location defined in class Location is the same type as the type parameter [T <: Location] of Route. scala编译器无法知道type T <: LocationLocation定义的[T <: Location]与Route的类型参数[T <: Location]的类型相同。

I think you will have to chage the signature of def distance(...) . 我想你将不得不改变def distance(...)的标志def distance(...) I am not sure, but it should work if you define T as type parameter of Location: 我不确定,但如果你将T定义为Location的类型参数,它应该可以工作:

abstract class Location[T <: Location[T]](val name: String) {
  def distance[T](that: T): Double
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM