Импорт не вносит имплициты в область видимости

Я сталкиваюсь с ошибкой о недостижимых implicits в области видимости:

Error:(38, 68) could not find implicit value for parameter strategy: XXX.NeoStrategy[T] (summoner: Summoner, v: String) => summoner.summonEvaluation[T](v)

Я реализую ответ Тима на этот вопрос: https://stackoverflow.com/a/56668734/3896166

Я попытался импортировать implicit object Стратегии в рамках TypeTable с помощью:

  import XXX.NeoStrategies._

но безуспешно.

Ниже приведены все файлы базовой логики, которые я хочу использовать:

  object TypeLib {
    sealed trait Type_top
    trait Type_A extends Type_top
    trait Type_B extends Type_top
  }
  trait NeoStrategy[T <: Type_top] {
    def evaluate(v: String, helper: Helper): Int
  }

  object NeoStrategies {
    implicit object NeoStrategy_A extends NeoStrategy[Type_A] {
      def evaluate(v: String, helper: Helper): Int = 1
    }
    implicit object NeoStrategy_B extends NeoStrategy[Type_B] {
      def evaluate(v: String, helper: Helper): Int = 2
    }
  }
  case class Helper(name: String) {
    def summonEvaluation[T <: Type_top](v: String)(implicit strategy: NeoStrategy[T]): Int = {
      strategy.evaluate(v, this)
    }
  }
  trait TypeOMap {
    protected def computeStuff[T <: Type_top]: (Helper, String) => Int
    protected val computeMap: Map[String, (Helper, String) => Int]
  }
  import XXX.NeoStrategies._

  trait TypeTable extends TypeOMap {
    override protected def computeStuff[T <: Type_top]: (Helper, String) => Int = {
      (helper: Helper, v: String) => helper.summonEvaluation[T](v)
    }
    override protected val computeMap = Map(
      "a" -> computeStuff[Type_A],
      "b" -> computeStuff[Type_B]
    )
  }
class Summoner extends TypeTable {

  def callsMapAndEvaluates(typeIdentifier: String, helper: Helper, param: String): Double = {

    computeMap(typeIdentifier)(helper, param)
  }
}
object StackO {

  def main(args: Array[String]): Unit = {

    val mySummoner = new Summoner

    // mySummoner allows the selecting of a given type with
    // its "typeIdentifier" input in combination with the "TypeTable" it extends
    val r = mySummoner.callsMapAndEvaluates("a", Helper("make it right"), "I, parameter")
  }
}

Это не первый раз, когда я использую implicits, но не с чем-то вроде computeMap выше. Тем не менее, я понимаю логику этого, но не могу сделать это правильно.

Как summoner.summonEvaluation[T](v) найти нужный implicit?


person wipman    schedule 26.06.2019    source источник


Ответы (2)


Просто добавьте привязку к контексту

override protected def computeStuff[T <: Type_top : NeoStrategy] ...

Кажется, вы хотите работать с одноэлементными типами. В Scala 2.12 + Shapeless

  import shapeless.Witness

  object TypeLib {
    sealed trait Type_top
    trait Type_A extends Type_top
    trait Type_B extends Type_top
  }

  import TypeLib._

  trait NeoStrategy[S <: String] {
    type T <: Type_top
    def evaluate(v: S, summoner: Summoner): Int
  }

  object NeoStrategy {
    type Aux[S <: String, T0 <: Type_top] = NeoStrategy[S] { type T = T0 }
    def mkStrategy[S <: String, T0 <: Type_top](f: (S, Summoner) => Int): Aux[S, T0] = new NeoStrategy[S] {
      override type T = T0
      override def evaluate(v: S, summoner: Summoner): Int = f(v, summoner)
    }

    implicit val NeoStrategy_A: NeoStrategy.Aux[Witness.`"a"`.T, Type_A] = mkStrategy((_, _) => 1)
    implicit val NeoStrategy_B: NeoStrategy.Aux[Witness.`"b"`.T, Type_B] = mkStrategy((_, _) => 2)
  }

  case class Summoner(name: String) {
    def summonEvaluation[S <: String](s: Witness.Aux[S])(implicit
      strategy: NeoStrategy[S]): Int = {
      strategy.evaluate(s.value, this)
    }
  }

  def main(args: Array[String]): Unit = {

    val mySummoner = Summoner("stack question")

    val r = mySummoner.summonEvaluation("a")
    val r1 = mySummoner.summonEvaluation("b")

    println(r) // 1
    println(r1) // 2
  }

В Скала 2.13

  object TypeLib {
    sealed trait Type_top
    trait Type_A extends Type_top
    trait Type_B extends Type_top
  }

  import TypeLib._

  trait NeoStrategy[S <: String with Singleton] {
    type T <: Type_top
    def evaluate(v: S, summoner: Summoner): Int
  }

  object NeoStrategy {
    type Aux[S <: String with Singleton, T0 <: Type_top] = NeoStrategy[S] { type T = T0 }
    def mkStrategy[S <: String with Singleton, T0 <: Type_top](f: (S, Summoner) => Int): Aux[S, T0] = new NeoStrategy[S] {
      override type T = T0
      override def evaluate(v: S, summoner: Summoner): Int = f(v, summoner)
    }

    implicit val NeoStrategy_A: NeoStrategy.Aux["a", Type_A] = mkStrategy((_, _) => 1)
    implicit val NeoStrategy_B: NeoStrategy.Aux["b", Type_B] = mkStrategy((_, _) => 2)
  }

  case class Summoner(name: String) {
    def summonEvaluation[S <: String with Singleton](s: S)(implicit
      value: ValueOf[S],
      strategy: NeoStrategy[S]): Int = {
      strategy.evaluate(s, this)
    }
  }

  def main(args: Array[String]): Unit = {

    val mySummoner = Summoner("stack question")

    val r = mySummoner.summonEvaluation("a")
    val r1 = mySummoner.summonEvaluation("b")

    println(r) // 1
    println(r1) // 2
  }
person Dmytro Mitin    schedule 26.06.2019
comment
Не могли бы вы воспроизвести проблему? Я добавил, как сказано, : NeoStrategy как в чертах TypeOMap, так и в чертах TypeTable. Я получаю эту ошибку: Error:(X) ambiguous implicit values: both object NeoStrategy_A in object NeoStrategies of type XXX.NeoStrategies.NeoStrategy_A.type and object NeoStrategy_B in object NeoStrategies of type XXX.NeoStrategies.NeoStrategy_B.type match expected type XXX.NeoStrategy[T] val r = mySummoner.summonEvaluation("a"). Я отредактировал свой первый пост, добавив еще несколько деталей. - person wipman; 26.06.2019
comment
@wipman Вы должны указать параметр типа: либо mySummoner.summonEvaluation[Type_A]("a"), либо mySummoner.summonEvaluation[Type_B]("a"). - person Dmytro Mitin; 26.06.2019
comment
Цель настройки состоит в том, чтобы иметь возможность вызывать NeoStrategy, не указывая type, а просто ключ (String) благодаря computeMap из TypeOMap. Это точка, развитая в связанном вопросе. У вас есть идея обходного пути? - person wipman; 26.06.2019
comment
@wipman Как вы ожидаете, что тип T будет выведен в mySummoner.summonEvaluation[T]("a")? Оба варианта mySummoner.summonEvaluation[Type_A]("a"), mySummoner.summonEvaluation[Type_B]("a") допустимы. - person Dmytro Mitin; 26.06.2019
comment
@wipman Кажется, вы хотите работать с одноэлементными типами (см. Обновление). - person Dmytro Mitin; 26.06.2019
comment
Спасибо за ваш ответ. Я полностью обидел вас плохой кодовой базой, она была совсем не правильной и в ней отсутствовали ключевые элементы. Является ли отредактированный вопрос более понятным? - person wipman; 27.06.2019
comment
@wipman Вам просто нужно добавить контекстную привязку T <: Type_top : NeoStrategy к TypeOMap#computeStuff и TypeTable#computeStuff. - person Dmytro Mitin; 27.06.2019
comment
@wipman В любом случае, весь ваш код с типами, имплицитами и т. Д. - это просто сложный способ сказать Map("a" -> 1, "b" -> 2). Поскольку ваш код mySummoner.callsMapAndEvaluates("c", Helper("make it right"), "I, parameter") компилируется и выбрасывает NoSuchElementException только во время выполнения (нет особого смысла возиться с типами и имплицитами, которые разрешаются во время компиляции). Я показал код, где mySummoner.summonEvaluation("c") не компилируется (в этом основной смысл типов и имплицитов). - person Dmytro Mitin; 27.06.2019
comment
@wipman Но если вы получите строку во время выполнения, мой подход не сработает. val a = "a"; mySummoner.summonEvaluation(a) не компилируется. - person Dmytro Mitin; 27.06.2019

Основная проблема заключается в следующем:

  override protected def computeStuff[T <: Type_top]: (Helper, String) => Int = {
    (helper: Helper, v: String) => helper.summonEvaluation[T](v) // implicit for NeoStrategy[T]...?
  }

Поскольку summonEvaluation[T] требует неявного аргумента типа NeoStrategy[T], это означает, что вы должны иметь его в области действия для любого T, являющегося подклассом Type_top. Однако NeoStrategies предоставляет только два экземпляра: один для Type_A и Type_B. Этого недостаточно для компилятора. Понятное дело - например, вы не указали NeoStrategy для

  • Type_top себя
  • подклассы Type_A и Type_B (совершенно законно создавать)

Есть два основных способа справиться с этим:

Задержка неявного разрешения

В соответствии с другим ответом, вместо того, чтобы пытаться разрешить неявное внутри computeStuff, добавьте туда также привязку к контексту. Если точка, в которой вы должны указать неявное, достигается только тогда, когда вы знаете, что такое T, вам не нужно будет предоставлять экземпляры для любого возможного подтипа.

Предоставление имплицитов для всех возможных подтипов

Если вы абсолютно хотите сохранить неявное разрешение внутри computeStuff, вам придется предложить метод

  implicit def getNeoStrategy[T <: Type_top] : NeoStrategy[T] = ???

К сожалению, выполнение этого, вероятно, потребует множества размышлений и возможных ошибок времени выполнения для пограничных случаев, поэтому я бы рекомендовал контекст, привязанный к computeStuff.

person Astrid    schedule 27.06.2019
comment
Спасибо за дополнительные пояснения. - person wipman; 27.06.2019