Самый прямой способ получить то, что вы хотите, - это переместить параметр типа в объявление признака. Это дает trait Foo[C]{...}
. Однако использование copy
в вашем transform
по-прежнему не сработает, поскольку черта 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)