Подтвердить что ты не робот

Невозможно доказать, что одноэлементные типы являются одноэлементными типами при генерации экземпляра класса типа

Предположим, что у меня есть класс типа, который доказывает, что все типы в копыте Shapeless являются одноточечными типами:

import shapeless._

trait AllSingletons[A, C <: Coproduct] {
  def values: List[A]
}

object AllSingletons {
  implicit def cnilSingletons[A]: AllSingletons[A, CNil] =
    new AllSingletons[A, CNil] {
      def values = Nil
    }

  implicit def coproductSingletons[A, H <: A, T <: Coproduct](implicit
    tsc: AllSingletons[A, T],
    witness: Witness.Aux[H]
  ): AllSingletons[A, H :+: T] =
    new AllSingletons[A, H :+: T] {
      def values = witness.value :: tsc.values
    }
}

Мы можем показать, что он работает с простым ADT:

sealed trait Foo
case object Bar extends Foo
case object Baz extends Foo

И затем:

scala> implicitly[AllSingletons[Foo, Bar.type :+: Baz.type :+: CNil]].values
res0: List[Foo] = List(Bar, Baz)

Теперь мы хотим объединить это с механизмом Shapeless Generic, который даст нам копродуктное представление нашего ADT:

trait EnumerableAdt[A] {
  def values: Set[A]
}

object EnumerableAdt {
  implicit def fromAllSingletons[A, C <: Coproduct](implicit
    gen: Generic.Aux[A, C],
    singletons: AllSingletons[A, C]
  ): EnumerableAdt[A] =
    new EnumerableAdt[A] {
      def values = singletons.values.toSet
    }
}

Я ожидаю, что implicitly[EnumerableAdt[Foo]] будет работать, но это не так. Мы можем использовать -Xlog-implicits для получения информации о том, почему:

<console>:17: shapeless.this.Witness.apply is not a valid implicit value for
  shapeless.Witness.Aux[Baz.type] because:
Type argument Baz.type is not a singleton type
              implicitly[EnumerableAdt[Foo]]
                        ^
<console>:17: this.AllSingletons.coproductSingletons is not a valid implicit
  value for AllSingletons[Foo,shapeless.:+:[Baz.type,shapeless.CNil]] because:
hasMatchingSymbol reported error: could not find implicit value for parameter
  witness: shapeless.Witness.Aux[Baz.type]
              implicitly[EnumerableAdt[Foo]]
                        ^
<console>:17: this.AllSingletons.coproductSingletons is not a valid implicit
  value for AllSingletons[Foo,this.Repr] because:
hasMatchingSymbol reported error: could not find implicit value for parameter
  tsc: AllSingletons[Foo,shapeless.:+:[Baz.type,shapeless.CNil]]
              implicitly[EnumerableAdt[Foo]]
                        ^
<console>:17: this.EnumerableAdt.fromAllSingletons is not a valid implicit
  value for EnumerableAdt[Foo] because:
hasMatchingSymbol reported error: could not find implicit value for parameter
  singletons: AllSingletons[Foo,C]
              implicitly[EnumerableAdt[Foo]]
                        ^
<console>:17: error: could not find implicit value for parameter e:
  EnumerableAdt[Foo]
              implicitly[EnumerableAdt[Foo]]
                        ^

Baz.type, очевидно, является одноэлементным типом. Мы можем попробовать разместить экземпляры Witness в области вручную только для удовольствия:

implicit val barSingleton = Witness[Bar.type]
implicit val bazSingleton = Witness[Baz.type]

И как-то теперь это работает:

scala> implicitly[EnumerableAdt[Foo]].values
res1: Set[Foo] = Set(Bar, Baz)

Я не понимаю, почему эти экземпляры будут работать в этом контексте, в то время как те, которые сгенерированы с помощью метода макросов Witness.apply (который мы использовали для их создания), этого не делают. Что здесь происходит? Есть ли удобный способ обхода, который не требует, чтобы мы вручную перечисляли конструкторы?

4b9b3361

Ответ 1

Это работает так же, как и с самого последнего бесформенного 2.1.0-SNAPSHOT.