Suppose I've got a type class that proves that all the types in a Shapeless coproduct are singleton types:
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 } }
We can show that it works with a simple ADT:
sealed trait Foo case object Bar extends Foo case object Baz extends Foo
And then:
scala> implicitly[AllSingletons[Foo, Bar.type :+: Baz.type :+: CNil]].values res0: List[Foo] = List(Bar, Baz)
Now we want to combine this with Shapeless's Generic
mechanism that'll give us a coproduct representation of our 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 } }
I'd expect implicitly[EnumerableAdt[Foo]]
to work, but it doesn't. We can use -Xlog-implicits
to get some information about why:
<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
obviously is a singleton type, though. We can try putting the Witness
instances in scope manually just for fun:
implicit val barSingleton = Witness[Bar.type] implicit val bazSingleton = Witness[Baz.type]
And somehow now it works:
scala> implicitly[EnumerableAdt[Foo]].values res1: Set[Foo] = Set(Bar, Baz)
I don't understand why these instances would work in this context while the ones generated by the Witness.apply
macro method (which we used to create them) don't. What's going on here? Is there a convenient workaround that doesn't require us to enumerate the constructors manually?
This works as written as of the most recent shapeless 2.1.0-SNAPSHOT.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With