scala shapeless implicits singleton-type

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



shapeless implicits (1)

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

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 el mecanismo Generic de Shapeless 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 } }

Esperaría implicitly[EnumerableAdt[Foo]] funcione, pero no es así. 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 es un tipo singleton. Podemos tratar de poner las instancias Witness 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 método macro Witness.apply (que usamos para crearlas) no lo hacen. ¿Que está pasando aqui? ¿Existe una solución conveniente que no requiera que enumeremos los constructores manualmente?