Неявное преобразование, при котором результирующий тип является проекцией типа на универсальный тип

#scala #implicit-conversion #implicit #type-projection

Вопрос:

У меня есть код scala 2.13, который в основном сводится к следующему

 import scala.language.implicitConversions

trait Base {
  type V
  def v: V
}

case class Derived(v: Int) extends Base {
  type V = Int
}


object TestImplicitConversion {
  implicit def getV[T <: Base](a: T): T#V = a.v

  val a: Int = Derived(5)
}

 

Здесь я бы ожидал , что компилятор будет использовать неявное преобразование getV для преобразования Derived в Int , но код не компилируется. Добавление вызова вручную в getV , приведет к компиляции кода. Может кто-нибудь помочь мне понять, почему преобразование, где в стандарте это объясняется.

Способ, который я нашел для выполнения такого преобразования, заключается в добавлении второго общего параметра и ограничения

 implicit def getV2[T <: Base, S](a: T)(implicit constraint: T#V =:= S): S = constraint(a.v)
 

в этой версии компилятор использует преобразование, и код компилируется.

Редактировать:

Альтернативное решение, предоставленное @user с использованием типа уточнения, действительно кажется лучшим подходом. Но на самом деле это не дает ответа на вопрос, почему его оригинальная реализация не работает. Поэтому мне все еще интересно понять, почему компилятор не использует неявное определение, когда явный вызов приведет к компиляции кода.

Комментарии:

1. Просто подсказка, старайтесь избегать использования проекции общего типа, это необоснованно , вот несколько примеров . Scala 3 отбрасывает проекцию общего типа. Лучше использовать тип, зависящий от пути, например: implicit def getV[T <: Base](a: T): a.V = a.v

2. @gianlucaaguzzi Замена проекции типа T#V типом, зависящим от пути a.V , не помогает. Scala 2.13.6.

3. @DmytroMitin, похоже, это работает на меня.

4. @пользователь, которого Вы удалили implicit . Конечно, с явным вызовом это работает.

5. @user Если вы удалите implicit , то нет необходимости заменять проекции типов типами, зависящими от пути. Явный вызов работает даже с проекцией типа.

Ответ №1:

Как упоминал в комментариях Джанлука Агуцци, проекция типа является необоснованной, и ее следует избегать. Более того, T это не конкретный тип, поэтому вы все равно не можете использовать проекции на нем. Вместо этого вы можете принимать только Base#V в качестве параметра типа и использовать тип уточнения для типа a :

 implicit def get[T](a: Base { type V = T }): T = a.v
 

Таким образом, вы можете избежать приведения и ввода проекций.

Скасти

Комментарии:

1.Существует гипотеза, что проекции типов остаются правильными до тех пор, пока они не смешиваются с типами нижних границ/пересечений github.com/lampepfl/dotty-feature-requests/issues/14 lptk.github.io/programming/2019/09/13/type-projection.html

2. Я не уверен в реальных потребностях OP, но первоначальный вопрос касался неявных преобразований.

3. @DmytroMitin Ах, это очень интересно! Будем надеяться, что это в конечном итоге будет реализовано в Scala 3.

4. @DmytroMitin Я удалил часть типа, зависящую от пути, и написал ответ только для Scala 2 (с помощью Scastie, который использует Scala 2 вместо Scala 3), я добавлю Scala 3 позже.

5. Я думаю, что этот подход, представленный здесь, лучше, чем тот, который использовался в первоначальном вопросе. Но я все равно хотел бы понять, почему оригинальная версия не работает. Потому что добавление вызова явно в scala 2.13 будет компилироваться и работать должным образом.