I'm trying to resolve an implicit then use the type stored in it to resolve a second implicit.
Here is the code:
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)
}
}
Calling it like this:
lazy val exploration = getOrEmpty[ExplorationStreamFilter](config) <--- ERROR 2: cannot find implicit here either
Compiling shows 2 errors:
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)
Why can't the compiler find the implicit? What's a solution for this?
This seems to work.
It uses the Aux pattern, and the Partially-applied trick.
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)
}
(I do not understand what is the purpose of the ReturnTypeRetriever
trait. But I leave it to preserve the original problem)