Rozważ ten prosty przykład:
trait Optimizer[+FParam, FRes] {
def optimize(
fn: (FParam) => FRes,
guesses: Seq[FParam] // <--- error
)
}
Nie kompiluje się, ponieważ
Typ kowariantny
FParam
występuje w pozycji sprzecznej z typemSeq[FParam]
domysłów wartości.
Ale sekwencja jest zdefiniowana jako trait Seq[+A]
, więc jakie jest źródło tej sprzeczności? (Pytanie 1)
I odwrotnie, rozważ ten prosty przykład z -FParam
:
trait Optimizer[-FParam, FRes] {
def optimize(
fn: (FParam) => FRes, // <--- error
guesses: Seq[FParam]
)
}
Typ przeciwstawny występuje w pozycji kowariantnej w typie
(FParam) => FRes
Ponownie ten sam paradoks: w Function1[-T1, R]
, parametr pierwszego typu jest wyraźnie sprzeczny, więc dlaczego FParam
w pozycji kowariantnej? (Pytanie 2)
Mogę rozwiązać ten problem, odwracając wariancję zgodnie z opisem w Dolne granice typów, ale dlaczego jest to konieczne, nie jest jasne.
trait Optimizer[+FParam, FRes] {
type U <: FParam
def optimize(
fn: (FParam) => FRes,
guesses: Seq[U]
)
}
Odpowiedzi:
2 dla odpowiedzi № 1Pytanie 1: +FParam
oznacza kowariant rodzaj FParam
i różni się od supertype podtyp FParam
. dla guesses
to się spodziewa cotravariant type
dla Seq
. Możesz to zrobić, wyraźnie zaznaczając supertype
z FPParam
w tym:
def optimize[B >: FParam](fn: (B) => FRes, guesses: Seq[B]) // B is the super type of FParam
lub jak SeqViewLike.
więc po co guesses
to się spodziewa cotravariant type
dla Seq
? na przykład:
trait Animal
case class Dog() extends Animal
case class Cat() extends Animal
val o = new Optimizer2[Dog, Any]
val f: Dog => Any = (d: Dog) => ...
o.optimize(f, List(Dog(), Cat())) // if we don"t bind B in `guesses` for supertype of `FParam`, it will fail in compile time, since the `guesses` it"s expecting a `Dog` type, not the `Animal` type. when we bind it to the supertype of `Dog`, the compiler will infer it to `Animal` type, so `cotravariant type` for `Animal`, the `Cat` type is also matched.
pytanie 2: -FParam
oznacza kotravairant rodzaj FParam
i różni się od nadtypu FParam
do tego podtyp.dla fn: Function1[-T1, +R]//(FParam) => FRes
to się spodziewa kowariant wpisz do tego. Możesz to zrobić odwrotnie Pytanie 1 wpisz stan, np .:
def optimize[B <: FParam](fn: (B) => FRes, guesses: Seq[B]) // B is the sub type of FParam
0 dla odpowiedzi nr 2
Problem polega na tym, że FParam nie jest używany bezpośrednio. Jest to argumentem optimize
i jako taka zmienia się jego wariancja. Aby to zilustrować, spójrzmy na typ optimize
(widzieć val optim
):
trait Optimizer[+FParam, FRes] {
type U <: FParam
def optimize(
fn: (FParam) => FRes,
guesses: Seq[U]
)
val optim: Function2[
Function1[
FParam,
FRes
],
Seq[U],
Unit
] = optimize
}