Estoy tratando de resolver un implícito y luego usar el tipo almacenado en él para resolver un segundo implícito.
Aquí está el código:

sealed trait ReturnTypeRetriever[T] {
  type ReturnType
}
object ReturnTypeRetrievers {
  implicit val expl = new ReturnTypeRetriever[ExplorationStreamFilter] {
    type ReturnType = SimpleFilter[Context, Context]
  }
}

sealed trait Retriever[T, +R] {
  def get(config: Config): R //SimpleFilter[Context, Context] 
}
object Retrievers {
  // implementation here for T=ExplorationStreamFilter and R=SimpleFilter[Context, Context]
  implicit val expl = new Retriever[ExplorationStreamFilter, SimpleFilter[Context, Context]] {
    override def get(config: Config) = {..}
  }

  // putting it all together
  def getOrEmpty[A](config: Config)(implicit evret: ReturnTypeRetriever[A]) = {
    val ev = implicitly[Retriever[A, evret.ReturnType]]  <-- ERROR 1: cannot find implicit defined above ^
    ev.get(config)
  }
}

Llamándolo así:

lazy val exploration = getOrEmpty[ExplorationStreamFilter](config) <--- ERROR 2: cannot find implicit here either

La compilación muestra 2 errores:

ERROR 1 message: could not find implicit value for parameter e: Retriever[A,evret.ReturnType]
[error]   val ev = implicitly[Retriever[A, evret.ReturnType]]

ERROR 2 message: could not find implicit value for parameter evret: ReturnTypeRetriever[ExplorationStreamFilter]
[error]   lazy val exploration = getOrEmpty[ExplorationStreamFilter](config)

¿Por qué el compilador no puede encontrar lo implícito? ¿Cuál es la solución para esto?

0
Adrian 3 oct. 2019 a las 02:54

1 respuesta

La mejor respuesta

Esto parece funcionar.
Utiliza el patrón Aux y el truco Parcialmente aplicado .

sealed trait ReturnTypeRetriever[T] {
  type ReturnType
}

// It should be named equally to the trait, in order to be a companion.
// That way, implicits will be in scope.
object ReturnTypeRetriever {
  // Aux pattern!
  // Used to transform a type member, into a type parameter, for better inference.
  type Aux[T, R] = ReturnTypeRetriever[T] { type ReturnType = R }

  // Implicits must always have explicit type signatures.
  implicit final val expl: Aux[ExplorationStreamFilter, SimpleFilter[Context, Context]] =
    new ReturnTypeRetriever[ExplorationStreamFilter] {
      override final type ReturnType = SimpleFilter[Context, Context]
    }
}

sealed trait Retriever[T, +R] {
  def get(config: Config): R
}

object Retriever {
  implicit final val expl: Retriever[ExplorationStreamFilter, SimpleFilter[Context, Context]] =
    new Retriever[ExplorationStreamFilter, SimpleFilter[Context, Context]] {
      override final def get(config: Config): SimpleFilter[Context, Context] =
        ???
    }

  // Ideally, you should make this class private[package]
  // Where package is the package in which this is defined.      
  final class GetOrEmptyPartiallyApplied[A](private val dummy: Boolean) extends AnyVal {
    def apply[R](config: Config)
                (implicit rtr: ReturnTypeRetriever.Aux[A, R], retriever: Retriever[A, R]): R =
      retriever.get(config)
  }

  // Partially-applied trick!
  // Used to allow partial application of a type.
  // User only needs to specify A, the compiler will infer R.
  def getOrEmpty[A]: GetOrEmptyPartiallyApplied[A] =
    new GetOrEmptyPartiallyApplied(dummy = true)
}

(No entiendo cuál es el propósito del rasgo ReturnTypeRetriever. Pero lo dejo para preservar el problema original)

2
Luis Miguel Mejía Suárez 3 oct. 2019 a las 03:28