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

Предположим, что у меня есть класс типа, который доказывает, что все типы в копыте 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 (который мы использовали для их создания), этого не делают. Что здесь происходит? Есть ли удобный способ обхода, который не требует, чтобы мы вручную перечисляли конструкторы?

Ответ 1

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