У меня есть неявное преобразование - ниже - которое кажется, что оно определенно должно работать, но определенно нет. Кто-нибудь может пролить свет? Я знаю, что implicitly иногда может выйти из строя, когда используются уточнения типов - в этом ли проблема?
trait GetItem[A[_], T, R] {
type Out
def ret(a: A[T], ref: R): Out
}
object GetItem {
implicit def ifRefIsInt[A[_], T]: GetItem[A, T, Int] { type Out = A[T] } = new GetItem[A, T, Int] {
type Out = A[T]
def ret(a: A[T], ref: Int): Out = a
}
}
import GetItem._
//this works fine:
val t: GetItem[List, Double, Int] { type Out = List[Double] } = ifRefIsInt[List, Double]
// so does this:
implicitly[GetItem[List, Double, Int] { type Out = List[Double] }](t)
// this does not:
implicitly[GetItem[List, Double, Int] { type Out = List[Double] }]
// Could not find implicit parameter for value e: Example.Main.GetItem[List, Double, Int]{type Out = List[Double]}
Любая помощь очень ценится, я смотрел на это в течение некоторого времени без особого успеха.
На всякий случай в Дотти работает scastie.scala-lang.org/ItGtjBaZQrSWIX9wXvCyy Dmytro Mitin 13.11.2020
На самом деле здесь нет неявного преобразования, поэтому я редактирую заголовок. Dmytro Mitin 13.11.2020
Кажется, это еще один пример чрезмерно ограниченных имплицитов (123456). Кажется, это слишком много для имплицитов за один шаг. Компилятору не нравятся сложные типы, такие как (A, B), H :: L и A[T] (в нашем случае) при уточнении типа. Если мы заменим
implicit def ifRefIsInt[A[_], T]: GetItem[A, T, Int] { type Out = A[T] } =
new GetItem[A, T, Int] {
type Out = A[T]
def ret(a: A[T], ref: Int): Out = a
}
с участием
implicit def ifRefIsInt[A[_], T, O](implicit
ev: A[T] =:= O
): GetItem[A, T, Int] { type Out = O } = new GetItem[A, T, Int] {
type Out = O
def ret(a: A[T], ref: Int): Out = a
}
тогда
implicitly[GetItem.Aux[List, Double, Int, List[Double]]]
implicitly[GetItem[List, Double, Int] { type Out = List[Double] }]
Компилятор почему-то проглатывает предупреждение (при включенном -Xlog-implicits). Если я запускаю неявный поиск вручную
import scala.language.experimental.macros
import scala.reflect.macros.{whitebox, contexts}
def foo[A]: Unit = macro fooImpl[A]
def fooImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val context = c.asInstanceOf[contexts.Context]
val global: context.universe.type = context.universe
val analyzer: global.analyzer.type = global.analyzer
val callsiteContext = context.callsiteTyper.context
val typ = weakTypeOf[A]
val search = new analyzer.ImplicitSearch(
tree = EmptyTree.asInstanceOf[global.Tree],
pt = typ.asInstanceOf[global.Type],
isView = false,
context0 = callsiteContext.makeImplicit(reportAmbiguousErrors = true),
pos0 = c.enclosingPosition.asInstanceOf[scala.reflect.internal.util.Position]
)
println(s"allImplicits=${search.allImplicits}")
q""
}
тогда
foo[GetItem[List, Double, Int] { type Out = List[Double] }]
выдает предупреждение
App.this.GetItem.ifRefIsInt is not a valid implicit value for App.GetItem[List,Double,Int]{type Out = List[Double]} because:
hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
found : [A[_], T]App.GetItem[A,T,Int]{type Out = A[T]}
required: App.GetItem[List,Double,Int]{type Out = List[Double]}
scalac: allImplicits=List()
т.е. A, T не подразумеваются.
Ответ 2 · рейтинг 2 · Luis Miguel Mejía Suárez
Не уверен, почему это не работает, но хороший способ решить такие проблемы - использовать шаблон Aux для поднятия элементов типа в параметр типа, улучшающий разрешение.
trait GetItem[A[_], T, R] {
type Out
def ret(a: A[T], ref: R): Out
}
object GetItem {
type Aux[A[_], T, R, O] = GetItem[A, T, R] { type Out = O }
implicit def ifRefIsInt[A[_], T]: Aux[A, T, Int, A[T]] = new GetItem[A, T, Int] {
type Out = A[T]
def ret(a: A[T], ref: Int): Out = a
}
}