无法证明单例类型在生成类型类实例时是单例类型

52
假设我有一个类型类,证明Shapeless coproduct中的所有类型都是单例类型:
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
    }
}

我们可以通过一个简单的 ADT 来展示它是如何工作的:
sealed trait Foo
case object Bar extends Foo
case object Baz extends Foo

然后:

scala> implicitly[AllSingletons[Foo, Bar.type :+: Baz.type :+: CNil]].values
res0: List[Foo] = List(Bar, Baz)

现在,我们希望将此与Shapeless的Generic机制相结合,以便为我们的ADT提供一个coproduct表示:
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
    }
}

我希望implicitly[EnumerableAdt[Foo]]能够正常工作,但事实并非如此。我们可以使用-Xlog-implicits来获取一些关于失败原因的信息:

<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显然是一个单例类型。我们可以尝试手动将Witness实例放入范围内,仅仅是为了好玩:

implicit val barSingleton = Witness[Bar.type]
implicit val bazSingleton = Witness[Baz.type]

现在它不知何故可以工作:

scala> implicitly[EnumerableAdt[Foo]].values
res1: Set[Foo] = Set(Bar, Baz)

我不明白为什么这些实例在此上下文中有效,而由 Witness.apply 宏方法生成的实例(我们用它们创建)则不起作用。这是怎么回事?有没有方便的解决办法,不需要手动枚举构造函数?


1
我最近在这个区域修复了一些错误… 用最新的快照再试一次? - Miles Sabin
2
今天推送了更多的调整...第三次会成功吗? - Miles Sabin
1
它有效了!谢谢,Miles!当然,我会接受你提供的任何答案。 - Travis Brown
1
太棒了!感谢测试用例 :-) - Miles Sabin
1
对于因 EnumerableAdt 提供的行为而来到这个问题的人,请查看 sealerate - HT @TravisBrown - drstevens
显示剩余2条评论
1个回答

27

截至最近的shapeless 2.1.0-SNAPSHOT版本,这段代码是可行的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接