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?