Неявные классы преобразования для сглаживания типа. Типы функций не могут быть скомпилированы в Scala

В scala следующий код правильно компилируется:

class a {}
class b {}

object Main {

  implicit class Conv[f, t](val v: f ⇒ t) extends AnyVal {
    def conv = v
  }

  def main(args: Array[String]) {
    val m = (a: a) ⇒ new b
    m.conv
  }
}

Но по какой-то причине следующее не удается скомпилировать:

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 = (a: a) ⇒ new b
    m.conv
  }
}

со следующим сообщением:

value conv is not a member of a => b
    m.conv

Почему это происходит?

РЕДАКТИРОВАТЬ: Да, еще есть ошибка даже при

  val m: V[a,b] = new V[a,b] { def apply(a: a) = new b }

Ответ 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] разрешает эту и неявную работу разрешения снова.

Это вызывает вопрос о том, почему ковариация параметра второго типа не является проблемой для неявного разрешения, в то время как контравариантность параметра первого типа. Я играл и делал немного исследований. Я наткнулся на несколько интересных ссылок, что указывает на то, что определенно есть некоторые ограничения/проблемы вокруг неявного разрешения, особенно когда дело доходит до контравариантности.

Мне нужно было бы отнестись к кому-то со знанием внутренних компонентов компилятора 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
  }
}