Предположим, у меня есть класс типов, который доказывает, что все типы в совместном произведении Shapeless являются одноэлементными типами:
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
}
}
Мы можем показать, что это работает с простым АТД:
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)
Теперь мы хотим объединить это с механизмом Generic
Shapeless, который даст нам совместное представление нашего АТД:
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
(который мы использовали для их создания), — нет. Что тут происходит? Есть ли удобный обходной путь, который не требует от нас перечисления конструкторов вручную?
-Xlog-implicits
. Я узнал об этом из этого поста, и это просто помогло мне успешно отладить сложную бесформенную проблему. @TravisBrown: пишу вещи о программировании, которые остаются актуальными годами. :) - person Haspemulator   schedule 20.09.2016EnumerableAdt
, проверьте sealerate — HT @TravisBrown - person drstevens   schedule 15.05.2017