我可以使用类型类来获得子类的非混合、combine 的共享实现以及 return 子类类型吗?

Can I use typeclasses to get non-mixing of subclasses, shared implementation of combine, and also return subclass type?

猫和狗都是宠物,因此有年龄。我想定义一个 chooseOldest 方法,给定两只猫或两只狗,将选择最老的宠物,但当猫 时将拒绝选择出现了一只狗。

用 type类 实现的半群似乎让我完成了 90%,但我不知道是否可以定义 chooseOldest once;理论上,因为所有 Pet 都有一个年龄,所以不需要为每个 Pet 实现它,但实际上我不知道如何只定义一次,同时仍然返回子类化并保留“猫狗不混”规则:

trait Pet {
  def age: Int

  // When implementation is shared, it *doesn't* return the subclass type
  def _chooseOldest(rhs: Pet): Pet = if (age > rhs.age) this else rhs
}

case class Dog(age: Int) extends Pet
case class Cat(age: Int) extends Pet

trait Semigroup[A] {
  def chooseOldest(lhs: A, rhs: A): A
}

// When the implementation returns the subclass type, it can't be shared
implicit val dogSemigroup = new Semigroup[Dog] {
  override def chooseOldest(lhs: Dog, rhs: Dog): Dog = if (lhs.age > rhs.age) lhs else rhs
}

implicit val catSemigroup = new Semigroup[Cat] {
  override def chooseOldest(lhs: Cat, rhs: Cat): Cat = if (lhs.age > rhs.age) lhs else rhs
}

def chooseGeneric[P <: Pet](p1: P, p2: P)(implicit ev: Semigroup[P]): P = ev.chooseOldest(p1, p2)

def canChooseDogs(d1: Dog, d2: Dog)(implicit ev: Semigroup[Dog]): Dog = chooseGeneric(d1, d2)(ev)
def canChooseCats(c1: Cat, c2: Cat)(implicit ev: Semigroup[Cat]): Cat = chooseGeneric(c1, c2)(ev)

// Appropriately errors because there is no Semigroup[Pet]
def cantMixCatsAndDogs(c: Cat, d: Dog)(implicit ev: Semigroup[Pet]): Pet = chooseGeneric(c, d)(ev)

如果我无法解决这个问题,我会尝试 ,但我怀疑这里有一个我缺少的解决方案,因为这是我第一次涉足类型 [=42] =].

是否可以使用 type类 实现 3 个给定的要求?

  1. 猫狗不能“合体”
  2. chooseOldest 有一个实现
  3. 存在 chooseOldest returns 子类类型

你可以这样做:

sealed trait Pet {
  def age: Int
}
object Pet {
  final case class Dog(age: Int) extends Pet
  final case class Cat(age: Int) extends Pet

  sealed trait ChooseOldest[P <: Pet] {
    def apply(p1: P, p2: P): P
  }
  object ChooseOldest {
    given instance[P <: Pet](using NotGiven[P =:= Pet]): ChooseOldest[P] with
      override def apply(p1: P, p2: P): P =
        if (p1.age > p2.age) p1 else p2
  }
  
  extension [P <: Pet](self: P)(using ev: ChooseOldest[P])
    def chooseOldest(other: P): P =
      ev(self, other)
}

请注意,NotGiven[P =:= Pet] 确保不会存在允许混合类型的实例。