Импорт общих последствий из экземпляров классов

Я пытаюсь сделать универсальный неявный поставщик, который может создать неявное значение для данного типа, что-то в строках:

trait Evidence[T]

class ImplicitProvider[T] {
  class Implementation extends Evidence[T]
  implicit val evidence: Evidence[T] = new Implementation
}

Чтобы использовать это неявное, я создаю val provider = new ImplicitProvider[T] экземпляр где надо и импорт из него import provider._, Это прекрасно работает, пока есть только один экземпляр. Однако иногда последствия для нескольких типов необходимы в одном месте.

case class A()
case class B()

class Test extends App {
  val aProvider = new ImplicitProvider[A]
  val bProvider = new ImplicitProvider[B]

  import aProvider._
  import bProvider._

  val a = implicitly[Evidence[A]]
  val b = implicitly[Evidence[B]]
}

И это не компилируется с could not find implicit value for parameter а также not enough arguments for method implicitly ошибки.

Если я использую неявные значения от поставщиков напрямую, все снова начинает работать.

implicit val aEvidence = aProvider.evidence
implicit val bEvidence = bProvider.evidence

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

Можно ли это как-то достичь или я хочу слишком многого от компилятора?

1 ответ

Решение

Проблема заключается в том, что когда вы импортируете из обоих объектов, вы вводите две сущности, имена которых совпадают: evidence в aProvider а также evidence в bProvider, Компилятор не может устранить их неоднозначность, как из-за того, как он реализован, так и из-за того, что имплициты, которые уже могут быть загадочными, могли бы делать вещи, которые не могут быть выполнены явно (устранение неоднозначности между конфликтующими именами).

То, что я не понимаю, в чем смысл ImplicitProvider является. Вы можете вытащить Implementation класс на верхний уровень и иметь object где-то держит implicit vals.

class Implementation[T] extends Evidence[T]

object Evidence {
  implicit val aEvidence: Evidence[A] = new Implementation[A]
  implicit val bEvidence: Evidence[B] = new Implementation[B]
}

// Usage:
import Evidence._
implicitly[Evidence[A]]
implicitly[Evidence[B]]

Теперь нет названия столкновения.

Если вам нужно иметь фактический ImplicitProviderвместо этого вы можете сделать это:

class ImplicitProvider[T] { ... }

object ImplicitProviders {
  implicit val aProvider = new ImplicitProvider[A]
  implicit val bProvider = new ImplicitProvider[B]

  implicit def ImplicitProvider2Evidence[T: ImplicitProvider]: Evidence[T]
    = implicitly[ImplicitProvider[T]].evidence
}

// Usage
import ImplicitProviders._
// ...
Другие вопросы по тегам