使用子类的类型实现特质方法

3

我希望有一个trait Foo,提供了一个transform方法,可以对它应用函数。此外,我想强制要求实现该trait的类都拥有一个increment方法,该方法也能以某种方式转换对象。天真的解决方案:

trait Foo {
  def transform(fun: Foo => Foo): Foo = fun(this)
  def increment(n: Int): Foo
}

case class A(a: Int) extends Foo {
  // expecting available: transform(fun: A => A): A
  // to be implemented: increment(n: Int): A
  ...
}

上述方法行不通...继承的 transform 仍然期望 Foo => Foo,而不是 A => A,并且 increment 仍然需要返回 Foo,而不是 A
再试一次:
trait Foo {
  def transform[C <: Foo](fun: C => C): C = fun(this.asInstanceOf[C])
  def increment[C <: Foo](n: Int): C
}

case class A(a: Int) extends Foo {
  def increment(n: Int) = A(a + n)
}

A无法编译——它仍然会报告有关签名的问题。

去掉increment函数后,transform可以工作。但是,asInstanceOf看起来有点不安全。此外,我需要显式提供类型参数给transform

val a = A(1)
a.transform[A](x => x.copy(x.a + 1)) // returns A(2)

我想知道是否有一种聪明的方法可以完成它。

你确定你输入的是正确的吗?你能给出具体的错误信息吗?在我这里它似乎工作得很好:https://scalafiddle.io/sf/9gFk4sb/0 - Ethan
3
这可能会对你有帮助。https://tpolecat.github.io/2015/04/29/f-bounds.html 简而言之,我会使用一个类型类和可能是一个密封特质。 - Luis Miguel Mejía Suárez
@Ethan - 这个不行:https://scalafiddle.io/sf/fXI1Ser/0。 - Kombajn zbożowy
1
@LuisMiguelMejíaSuárez - 谢谢,这篇文章很长,我需要更深入地了解它,但看起来很有前途,正是我所需要的。 - Kombajn zbożowy
1个回答

5
将类型参数移至特质声明是获得所需内容最直接的方式,即使用trait Foo[C]{...}。然而,在你的transform中使用copy仍然无法奏效,因为Foo特质不知道任何扩展它的东西。你可以通过自类型(self typing)提供更多信息:
trait Foo[C] {
  this: C =>
    def transform(fun: C => C): C = fun(this)
    def increment(n: Int): C
}

case class A(a: Int) extends Foo[A] {
  def increment(n: Int) = A(a + n)
}
A extends Foo[A]这个语法在这里有点棘手,但是它能够工作,因为现在当你扩展Foo时,它会将类型信息提供回特质中。尽管如此,这仍然有点棘手。事实证明,我们可以使用一种称为类型类的技术来潜在地改进这些问题。首先,您需要设置特质。在类型类中,每种类型恰好有一个特质实现,因此每个方法还应该带上要操作的实例:
trait Foo[C] {
  def transform(c: C)(f: C => C): C
  def increment(c: C, inc: Int): C
}

接下来,在伴生对象中为您关心的类型设置类型类的实例:
case class A(a: Int)

object Foo {
  implicit val ATransform = new Foo[A] {
    def transform (base: A)(f: A => A) = f(base)
    def increment(base: A, inc: Int) = A(base.a+inc)
  }

  //Convenience function for finding the instance for a type.
  //With this, Foo[A] is equivalent to implicitly[Foo[A]]
  def apply[C](implicit foo: Foo[C]) = foo
}

现在我们可以按照以下方式使用类型类:
val b = A(3)
Foo[A].transform(b)(x=>x.copy(a=x.a+1)) //A(4)
Foo[A].increment(b,5) //A(8)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接