разрешить неявный, а затем использовать тип, хранящийся в нем, чтобы разрешить второй неявный - PullRequest
0 голосов
/ 03 октября 2019

Я пытаюсь разрешить неявное, а затем использовать тип, сохраненный в нем, для разрешения второго неявного.
Вот код:

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)
  }
}

Вызывая его так:

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

Компиляция показывает 2 ошибки:

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)

Почему компилятор не может найти неявное? Какое решение для этого?

1 Ответ

2 голосов
/ 03 октября 2019

Кажется, это работает.
Используется шаблон Aux и трюк Частично примененный .

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не понимаю, какова цель черты ReturnTypeRetriever. Но я оставляю это, чтобы сохранить первоначальную проблему)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...