Неявное неявное разрешение HList без формы - расходящееся неявное расширение - PullRequest
0 голосов
/ 21 ноября 2018

Это действительно беспокоит меня.Я получаю diverging implicit expansion for type Meta[Field2 :: HNil] ошибку, которую я пытаюсь скомпилировать следующим образом:

case class Field() extends StaticAnnotation
case class Group() extends StaticAnnotation
case class Message() extends StaticAnnotation

@Field case class Field1(value: String)
@Field case class Field2(value: String)
@Field case class Field3(value: String)
@Group case class Group1(field1: Field1, field2: Field2)
@Message case class Message1(field3: Field3, group1: Group1)

trait Meta[T]

object Meta {
  implicit val hNil: Meta[HNil] = new Meta[HNil] {}
  implicit def field[TField](implicit a: Annotation[Field, TField]): Meta[TField] = new Meta[TField] {}
  implicit def hcons[Head, Tail <: HList](implicit h: Meta[Head], t: Meta[Tail]) : Meta[H :: T] = new Meta[H :: T] {}
  implicit def group[TGroup, ParamList <: HList](implicit a: Annotation[Group, TGroup], g: Generic.Aux[TGroup, ParamList], p: Meta[ParamList]): Meta[TGroup] = new Meta[TGroup] {}
  implicit def message[TMessage, ParamList <: HList](implicit a: Annotation[Message, TMessage], g: Generic.Aux[TMessage, ParamList], p: Meta[ParamList]): Meta[TMessage] = new Meta[TMessage] {}
}

object TestApp extends App {
  // throws compile exception here...
  implicitly[Meta[Message1]]
}

1 Ответ

0 голосов
/ 21 ноября 2018

Рассмотрим процесс расширения Meta[Message1]:

  • При расширении Meta[Message1] с помощью message компилятору необходимо Meta[Field3 :: Group1 :: HNil]
  • Позже, при расширении Meta[Group1] с помощьюgroup ему нужно Meta[Field1 :: Field2 :: HNil]

Компилятор видит, что в этой ветке он уже имеет расширенный конструктор типов ::, по крайней мере, такой же сложности (т. Е. С таким же количеством элементов вHList).Таким образом, предполагается, что эта ветвь расширения приводит к бесконечному циклу и сообщает о неявной дивергенции.

Чтобы предотвратить такое поведение, вы можете использовать shapeless.Lazy.Из документации:

Оборачивает лениво вычисленное значение.Также обходит циклы во время неявного поиска или неправильные неявные расхождения, как показано ниже, и лениво удерживает соответствующее неявное значение.

Так что, чтобы исправить эту проблему, вы можете заключить в Lazy расширение Headв hcons:

implicit def hcons[Head, Tail <: HList](implicit 
  h: Lazy[Meta[Head]], 
  t: Meta[Tail]
): Meta[Head :: Tail] = 
  new Meta[Head :: Tail] {}

Обычно вы должны заключать в Lazy расширения голов в правилах HList и Coproduct, а также в расширения Repr в правилах Generic.Последнее здесь не обязательно, я думаю, потому что вам необходимо пройти правило hcons, которое уже имеет Lazy, чтобы перейти от одного Group или Message к другому).

...