Вы не можете использовать в качестве Foo[Bleh]
Foo[Blah]
поскольку Foo[Bleh]
не Foo[Blah]
. Вы должны сделать Foo
контравариантным на A
, чтобы использовать Foo[Bleh]
как Foo[Blah]
.
trait Foo[-A] {
def bar(a: A) = println(a) // to make Foo contravariant
}
Это работает просто отлично:
scala> foo1(Blah())
res0: Blah = Blah()
Ваш исходный код содержит ответ на ваш вопрос. Давайте предположим, что вы могли бы использовать оригинал Foo[Bleh]
в Foo[Blah]
:
def foo1[A:Foo](): A = implicitly[Foo[A]].bar
val b: Blah = foo1[Blah]()
В случае Foo[Bleh]
используется здесь вы получите Bleh
как результат bar
, но вы ожидаете Blah
и Bleh
не Blah
.
К счастью, компилятор не позволит использовать оригинал Foo[Bleh]
в Foo[Blah]
:
scala> trait Foo[-A] {
| def bar: A
| }
<console>:8: error: contravariant type A occurs in covariant position in type => A of method bar
def bar: A
^
Тип умозаключение
Это прекрасно работает:
foo1[Bleh](Blah())
Но компилятор не будет выводить тип параметр A
здесь как Bleh
. Для того, чтобы понять «почему» мы должны знать, что A:Foo
означает:
def foo1[A:Foo](a:A) = a // syntax sugar
def foo1[A](a:A)(implicit ev: Foo[A]) = a // same method
A:Foo
является синтаксисом для добавления неявного параметра.
Если у вас есть 2 группы параметров, то компилятор выдает тип первой группы и затем считает, что известные типы. Поэтому после вывода типа в первой группе параметров (a:A)
тип Blah
известен, а вторая группа параметров не может влиять на параметр типа.
Хороший вопрос, но имена переменных, которые вы выбрали, заставляют меня хотеть рвать. Разве они не были более наглядными, как SuperClass/SubClass? – Quantum7
'Foo',' Bar' и т. Д. Являются совершенно стандартными (и любимыми) в отрасли. –