如何指示 Scala 不要向上转换抽象类型?
How to instruct Scala not to upcast 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 中,一切都会崩溃:
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 目前是否缺少此功能?有什么办法让它起作用吗?
您的代码存在编译问题 - kennel.animal
无法解析,因为 class Kennel
没有将 animal
公开为 public 字段;这很容易通过在 animal
.
前面添加 val
来解决
似乎让您困扰的是 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
能够 return 确切的食物类型动物。
这是您可以试用的完整工作代码:
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"
}
参数多态性(即类型classes)是一种更好的建模方式
这个。这里的主要操作是你有一个动物,你必须
检索它最喜欢的食物的实例。把它变成一个类型class
并提供特定动物及其喜爱的食物的实例。
例如:
@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
class,但是
因为 Scala 的类型推断,你实际上不必拼写出来
类型。 @implicitNotFound
注释也给了你一个很好的
如果没有特定类型的 class 实例,则编译错误消息
动物及其食物。
请注意 Animal
和 Food
类型 实际上 不需要
动物和食物;这就是我们在这里使用的语义。如果你
以最通用的方式看它,这个 typeclass 实际上是
对于某些 A
和 B
.
只是类型 A => B
的函数
考虑斗牛犬:
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 中,一切都会崩溃:
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 目前是否缺少此功能?有什么办法让它起作用吗?
您的代码存在编译问题 - kennel.animal
无法解析,因为 class Kennel
没有将 animal
公开为 public 字段;这很容易通过在 animal
.
val
来解决
似乎让您困扰的是 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
能够 return 确切的食物类型动物。
这是您可以试用的完整工作代码:
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"
}
参数多态性(即类型classes)是一种更好的建模方式 这个。这里的主要操作是你有一个动物,你必须 检索它最喜欢的食物的实例。把它变成一个类型class 并提供特定动物及其喜爱的食物的实例。 例如:
@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
class,但是
因为 Scala 的类型推断,你实际上不必拼写出来
类型。 @implicitNotFound
注释也给了你一个很好的
如果没有特定类型的 class 实例,则编译错误消息
动物及其食物。
请注意 Animal
和 Food
类型 实际上 不需要
动物和食物;这就是我们在这里使用的语义。如果你
以最通用的方式看它,这个 typeclass 实际上是
对于某些 A
和 B
.
A => B
的函数