Невозможно доказать, что одноэлементные типы являются одноэлементными типами при создании экземпляра класса типов.

Предположим, у меня есть класс типов, который доказывает, что все типы в совместном произведении 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 (который мы использовали для их создания), — нет. Что тут происходит? Есть ли удобный обходной путь, который не требует от нас перечисления конструкторов вручную?


person Travis Brown    schedule 14.09.2014    source источник
comment
Недавно я исправил несколько ошибок в этой области... попробуйте еще раз с последним снимком?   -  person Miles Sabin    schedule 02.01.2015
comment
Хм, не повезло — тот же результат.   -  person Travis Brown    schedule 02.01.2015
comment
Сегодня добавили больше настроек ... в третий раз повезло?   -  person Miles Sabin    schedule 12.01.2015
comment
Оно работает! Спасибо, Майлз! Я приму любой ответ, который вы хотите сделать, конечно.   -  person Travis Brown    schedule 12.01.2015
comment
Потрясающий! Спасибо за пробник :-)   -  person Miles Sabin    schedule 12.01.2015
comment
+1 за -Xlog-implicits. Я узнал об этом из этого поста, и это просто помогло мне успешно отладить сложную бесформенную проблему. @TravisBrown: пишу вещи о программировании, которые остаются актуальными годами. :)   -  person Haspemulator    schedule 20.09.2016
comment
Для тех, кто остановился на этом вопросе из-за поведения, обеспечиваемого EnumerableAdt, проверьте sealerate — HT @TravisBrown   -  person drstevens    schedule 15.05.2017


Ответы (1)


Это работает так, как написано в самой последней бесформенной версии 2.1.0-SNAPSHOT.

person Miles Sabin    schedule 12.01.2015