52

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?

6
  • 1
    I fixed a few bugs in this area recently ... try again with the latest snapshot? Jan 2, 2015 at 10:07
  • Hmm, no luck—same result. Jan 2, 2015 at 19:00
  • 2
    More tweaks pushed today ... third time lucky? Jan 11, 2015 at 22:59
  • 1
    It works! Thanks, Miles! I'll accept any answer you care to make, of course. Jan 12, 2015 at 4:25
  • 1
    For those ending up at this question because of the behavior provided by EnumerableAdt, check out sealerate - HT @TravisBrown
    – drstevens
    May 15, 2017 at 19:38

1 Answer 1

27

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.