Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't prove that singleton types are singleton types while generating type class instance

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?

like image 333
Travis Brown Avatar asked Sep 14 '14 21:09

Travis Brown


1 Answers

This works as written as of the most recent shapeless 2.1.0-SNAPSHOT.

like image 57
Miles Sabin Avatar answered Sep 24 '22 19:09

Miles Sabin