gpt4 book ai didi

scala - 使用动态/具体类型初始化类型变量

转载 作者:行者123 更新时间:2023-12-01 08:44:30 27 4
gpt4 key购买 nike

我正在学习 Scala,我试图创建一个类型类来解决“每个动物都吃食物,但食物的类型取决于动物”的问题。我有一个带有上下文边界的 Eats 类型类:

trait Eats[A <: Animal, B <: Edible]

object Eats {
def apply[A, B]: Eats[A, B] = new Eats[A, B] {}
}

AnimalEdible 都是抽象类。 (简化的)Animal 界面看起来像这样

abstract class Animal {
type This // concrete type
def eat[A <: Edible](food: A)(implicit e: Eats[This, B]) = // ...
}

我的目标是仅当给定类型的动物和食物存在实例(范围内的隐式值)时才允许以 animal.eat(food) 的形式调用。为此,我创建了一个 EatingBehaviour 对象,它基本上包含所有关系的实例。例如。声明奶牛吃草我加了一行

implicit val cowEatsGrass = Eats[Cow, Grass]

类似于您在 Haskell 中编写 instance Eats Cow Grass 的方式。但是,现在我需要为 Animal 类的所有子类型指定抽象类型 This 以使 Animal 接口(interface)中的签名起作用:

class Cow extends Animal { type This = Cow }

这是多余的。

因此我的问题:我可以在 Animal 中以某种方式初始化类型变量 This 以便它始终反射(reflect)具体类型,类似于如何我可以使用 getClass 询问动态类型吗?

最佳答案

如果您传递第一个操作数 a: A,则不会出现此问题到有机会推断外部可见类型的方法/类构造函数A :

trait Animal
trait Eats[A <: Animal, B <: Animal]

object Eats {
def apply[A <: Animal, B <: Animal]: Eats[A, B] = new Eats[A, B] {}
}

implicit class EatsOps[A <: Animal](a: A) {
def eat[B <: Animal](food: B)(implicit e: Eats[A, B]) =
printf(s"%s eats %s\n", a, food)
}

case class Cat() extends Animal
case class Bird() extends Animal
case class Worm() extends Animal

implicit val e1 = Eats[Cat, Bird]
implicit val e2 = Eats[Bird, Worm]

val cat = Cat()
val bird = Bird()
val worm = Worm()

// c eat c // nope
cat eat bird
// c eat w // nope

// b eat c // nope
// b eat b // nope
bird eat worm

// w eat c // nope
// w eat b // nope
// w eat w // nope

这里,EatsOps[A <: Animal]可以先推断出什么A是,那么在 eat[B <: Animal]它可以推断出什么 B是,并使用有关 A 的信息和 B插入正确的隐式。没有类型成员,扩展 Animal 时无需执行任何操作.

这有点像 XY 问题的 X 解决方案。而且,是的,我重复使用了 Animal而不是 Food ...


更新

如果您想访问特定 Animal 的一些私有(private)方法调用 eat 时的实现,通常的做法是将所有基本功能移至 Eats trait,然后提供 Eats 的实例在特定 Animal 的伴随对象中.例如,下面是我们如何让 Cat做它的不可思议private真正吃东西之前的东西Bird :

trait Eats[A <: Animal, B <: Animal] {
def apply(a: A, food: B): Unit
}

object Eats {
def apply[A <: Animal, B <: Animal]: Eats[A, B] = new Eats[A, B] {
def apply(animal: A, food: B) = println(s"${animal} eats ${food}")
}
}

implicit class EatsOps[A <: Animal](animal: A) {
def eat[B <: Animal](food: B)(implicit e: Eats[A, B]) = e(animal, food)
}

case class Cat() extends Animal {
private def privateCatMethod(b: Bird): Unit = {}
}

object Cat {
implicit val catsEatBirds: Eats[Cat, Bird] = new Eats[Cat, Bird] {
def apply(c: Cat, b: Bird): Unit = {
c.privateCatMethod(b)
println(s"{c} eats {b}")
}
}
}

其余代码保持不变,除了不需要 e1: Eats[Cat, Bird]没有了。

关于scala - 使用动态/具体类型初始化类型变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57018190/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com