Ответ 1
В вашем первом примере val v: f => t
выводится на подпись типа [-A, +B]
, потому что это сокращение для функции одного параметра. Function1
имеет сигнатуру типа Function1[-A, +B]
. Итак, тип, который контравариантен в параметре A
и ковариант в параметре B
.
Тогда лямбда-функция, (a: a) => new b
позже в коде, имеет тип, вычисленный как функция от a до b. Таким образом, подпись типа идентична, и неявное разрешение работает.
В вашем втором примере введите V[f, t] = f => t
и созданный из него параметр: val v: V[f, t]
, их тип явно указан как V[f, t]
. Функция f => t
по-прежнему будет [-A, +B]
, но вы явно ограничиваете свои типы инвариантными в сигнатуре типа, поэтому тип V
является инвариантным в обоих параметрах типа.
Позже, когда вы объявите: val m = (a: a) => new b
, сигнатура типа этого будет по-прежнему [-A, +B]
, как в первом примере. Неявное разрешение не работает, потому что val является контравариантным в своем первом типе параметра, но тип V
является инвариантным в своем первом типе.
Изменение сигнатуры типа V на V[-f, +t]
или V[-f, t]
разрешает эту и неявную работу разрешения снова.
Это вызывает вопрос о том, почему ковариация параметра второго типа не является проблемой для неявного разрешения, в то время как контравариантность параметра первого типа. Я играл и делал немного исследований. Я наткнулся на несколько интересных ссылок, что указывает на то, что определенно есть некоторые ограничения/проблемы вокруг неявного разрешения, особенно когда дело доходит до контравариантности.
- A Scala Билет, связанный с контравариантностью и неявным разрешением.
- Длительное обсуждение групп google, связанных с билетом
- Некоторый код обходного пути для контравариантности и неявных разрешений в некоторых сценариях.
Мне нужно было бы отнестись к кому-то со знанием внутренних компонентов компилятора Scala и механики разрешения для более подробной информации, но, похоже, это противоречит некоторым ограничениям вокруг неявного разрешения в контексте Contravariance.
Для вашего третьего примера, я думаю, вы имеете в виду:
class a {}
class b {}
object Main {
type V[f, t] = f => t
implicit class Conv[f, t](val v: V[f, t]) extends AnyVal {
def conv = v
}
def main(args: Array[String]) {
val m: V[a,b] = new V[a,b] { def apply(a: a) = new b }
m.conv // does not compile
}
}
Это интересный вопрос, и я думаю, что это несколько другая причина. Типовые псевдонимы ограничены тем, что они могут изменить, когда дело доходит до дисперсии, но допускает более строгое отклонение. Я не могу точно сказать, что здесь происходит, но вот интересный вопрос, связанный с псевдонимами типов и дисперсией.
Учитывая сложность неявного разрешения, в сочетании с дополнительными факторами дисперсии объявления типа по сравнению с подразумеваемой дисперсией Function1, я подозреваю, что компилятор просто не в состоянии разрешить что-либо в этом конкретном сценарии.
Переход на:
implicit class Conv(val v: V[_, _]) extends AnyVal {
def conv = v
}
означает, что он работает во всех сценариях, потому что вы в основном говорите компилятору, что для целей неявного класса Conv вам не нужна дисперсия параметров типа на V.
например: также работает
class a {}
class b {}
object Main {
type V[f, t] = f ⇒ t
implicit class Conv(val v: V[_, _]) extends AnyVal {
def conv = v
}
def main(args: Array[String]) {
val m = (a: a) ⇒ new b
m.conv
}
}