이 암시 적 해결이 실패하는 이유는 무엇입니까?

Nov 13 2020

아래에 암시 적 변환이 있습니다. 확실히 작동해야하는 것처럼 느껴지지만 확실히 그렇지 않습니다. 누구든지 빛을 비출 수 있습니까? 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]}

많은 도움을 주셔서 감사합니다. 나는 이것을 거의 성공하지 못한 채 얼마 동안 쳐다보고 있습니다.

답변

2 DmytroMitin Nov 13 2020 at 10:46

과도하게 구속 된 암시 적 ( 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 LuisMiguelMejíaSuárez Nov 13 2020 at 09:24

왜 그렇게 작동하지 않는지 확실하지 않지만 이러한 종류의 문제를 해결하는 좋은 기술은 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] = ...