Pregunta No se puede probar que los tipos singleton son tipos singleton mientras se genera la instancia de clase de tipo


Supongamos que tengo una clase de tipo que demuestra que todos los tipos en un coproducto Shapeless son de tipo singleton:

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

Podemos demostrar que funciona con un ADT simple:

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

Y entonces:

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

Ahora queremos combinar esto con Shapeless Generic mecanismo que nos dará una representación coproducida de nuestro 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
    }
}

Yo esperaría implicitly[EnumerableAdt[Foo]] para trabajar, pero no es así. Nosotros podemos usar -Xlog-implicits para obtener información sobre por qué:

<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 obviamente es un tipo singleton, sin embargo. Podemos intentar poner el Witness instancias en el alcance manualmente solo por diversión:

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

Y de alguna manera ahora funciona:

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

No entiendo por qué estas instancias funcionarían en este contexto mientras que las generadas por el Witness.apply método macro (que usamos para crearlos) no. ¿Que está pasando aqui? ¿Existe una solución conveniente que no requiera que enumeremos los constructores manualmente?


48
2017-09-14 21:58


origen


Respuestas:


Esto funciona como está escrito con la información más reciente 2.1.0-SNAPSHOT.


22
2018-01-12 19:24