Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Почему это неявное разрешение не работает?

Автор вопроса: Chris J HarrisОтветов: 2

У меня есть неявное преобразование - ниже - которое кажется, что оно определенно должно работать, но определенно нет. Кто-нибудь может пролить свет? Я знаю, что 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

scalaimplicittypeclass

Ответы

Ответ 1 · рейтинг 2 · Dmytro Mitin

Кажется, это еще один пример чрезмерно ограниченных имплицитов (1 2 3 4 5 6). Кажется, это слишком много для имплицитов за один шаг. Компилятору не нравятся сложные типы, такие как (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] }]

скомпилировать: https://scastie.scala-lang.org/P5iXP2ZfQUCKEIMukYyqIg (Scala 2.13.3 )

Компилятор почему-то проглатывает предупреждение (при включенном -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
  }
}

Что вы можете проверить так:

implicitly[GetItem.Aux[List, Double, Int, List[Double]]]
// res: GetItem.Aux[List, Double, Int, List[Double]] = ...

implicitly[GetItem[List, Double, Int]]
// res: GetItem[List, Double, Int] = ...

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы