如何指示Scala不要向上转换抽象类型?

时间:2017-02-27 22:17:28

标签: scala types abstract-type

考虑斗牛犬:

trait Animal {
  type Food

  def defaultFood(): Food
}
class Bulldog extends Animal {
  type Food = Steak

  ... implementations ...
}

函数Bulldog.defaultFood()对于编译器来说效果很好(虽然我的语法高亮显示器出错了,这没什么大不了的):

val bulldog = new Bulldog()
val df: bulldog.Food = bulldog.defaultFood()

然而,如果斗牛犬被封闭在另一个类中,那么一切都会崩溃:

class Kennel(val animal: Animal) {
}
def getSupply(kennel: Kennel): kennel.animal.Food = {
  ... implementation ...
}

val kennel = new Kennel(bulldog)
val df2: bulldog.Food = getSupply(kennel)

Scala编译器将抛出编译错误:

type mismatch;
 found   : Option[kennel.animal.V] where val kennel: Kennel
 required: bulldog.Food

Scala目前是否缺少此功能?有没有办法让它发挥作用?

2 个答案:

答案 0 :(得分:3)

您的代码存在编译问题 - kennel.animal无法解析,因为类Kennel未将animal公开为公共字段;通过在val前添加animal可以轻松解决此问题。

似乎在困扰你的是Kennel对基础动物一无所知,除了它是Animal。通过斗牛犬被视为传递了一些动物。

尝试向Kennel添加更多类型信息:

class Kennel[A <: Animal](val animal: A) {  }

def getSupply[A <: Animal](kennel: Kennel[A]): kennel.animal.Food

val kennel = new Kennel(bulldog)
val df2: bulldog.Food = getSupply(kennel)

现在Kennel知道animal的确切类型(例如Bulldog),getSupply能够返回该动物的确切食物类型。

以下是您可以试用的完整工作代码:

trait Steak {
  override def toString() = "steak"
}

trait Animal {
  type Food
  def defaultFood(): Food
}

class Bulldog extends Animal {
  type Food = Steak
  def defaultFood() = new Steak {}
}

class Kennel[A <: Animal](val animal: A)

object Test extends App {

  def getSupply[A <: Animal](kennel: Kennel[A]): kennel.animal.Food = kennel.animal.defaultFood()

  val bulldog = new Bulldog()
  val kennel = new Kennel(bulldog)
  val df2: bulldog.Food = getSupply(kennel)

  println(df2) // prints "steak"
}

答案 1 :(得分:0)

参数多态(即类型类)是一种更好的建模方法 这个。这里的主要操作是你有动物,你必须这样做 检索其最喜欢的食物的实例。将其转换为类型类 并为特定动物及其喜爱的食物提供实例。 E.g:

@annotation.implicitNotFound(
  "Couldn't confirm that ${Animal}'s favourite food is ${Food}")
trait DefaultFood[Animal, Food] { def apply(animal: Animal): Food }
object DefaultFood {
  /** Helper to easily implement typeclass instances. */
  class Impl[Animal, Food](getFood: Animal => Food)
    extends DefaultFood[Animal, Food] {
    override def apply(animal: Animal): Food = getFood(animal)
  }
}

class Bulldog
object Bulldog {
  type Steak = String // Or whatever.

  implicit val defaultFood: DefaultFood[Bulldog, Steak] =
    new DefaultFood.Impl(_ => "Steak")
}

class Kennel[Animal, Food](
  animal: Animal)(implicit defaultFood: DefaultFood[Animal, Food]) {
  def getSupply: Food = defaultFood(animal)
}

object Test {
  val kennel = new Kennel(new Bulldog) // Kennel[Bulldog, Bulldog.Steak]
}

这会将泛型类型向前传播到Kennel类,但是 因为Scala的类型推断,你实际上不必拼写出来 类型。 @implicitNotFound注释也为您提供了很好的帮助 如果某个特定的没有类型类实例,则编译错误消息 动物及其食物。

请注意AnimalFood类型 动物和食物;这只是我们在这里使用的语义。如果你 以最通用的方式查看它,这个类型类实际上是 对于某些A => BA而言,只是B类型的函数。