简体   繁体   English

无形HList隐式分辨率-发散的隐式扩展

[英]Shapeless HList implicit resolution - diverging implicit expansion

This is really bugging me. 这真是困扰我。 I am getting a diverging implicit expansion for type Meta[Field2 :: HNil] error which I try and compile the following: 我收到diverging implicit expansion for type Meta[Field2 :: HNil]错误的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]]
}

Consider the process of expanding Meta[Message1] : 考虑扩展Meta[Message1]

  • When expanding Meta[Message1] with message the compiler needs Meta[Field3 :: Group1 :: HNil] 使用message扩展Meta[Message1]时,编译器需要Meta[Field3 :: Group1 :: HNil]
  • Later, when expanding Meta[Group1] with group it needs Meta[Field1 :: Field2 :: HNil] 稍后,在使用group扩展Meta[Group1]时,需要Meta[Field1 :: Field2 :: HNil]

The compiler sees, that in this branch it has already expanded type constructor :: of at least the same complexity (ie, with the same number of elements in the HList ). 编译器看到,在该分支中,它已经扩展了类型构造器:: ,其复杂度至少相同(即, HList的元素数相同)。 So it assumes, that this expansion branch results in an infinite loop, and reports implicit divergence. 因此,它假定此扩展分支会导致无限循环,并报告隐式发散。

To prevent this behaviour you can use shapeless.Lazy . 要防止此行为,可以使用shapeless.Lazy From the documentation: 从文档中:

Wraps a lazily computed value. 包装延迟计算的值。 Also circumvents cycles during implicit search, or wrong implicit divergences as illustrated below, and holds the corresponding implicit value lazily. 还可以避开隐式搜索过程中的循环,或错误的隐式发散度,如下所示,并懒惰地保存相应的隐式值。

So to fix this problem you can wrap in Lazy the expansion of Head in hcons : 因此,要解决此问题,您可以在hcons Head的扩展包装在Lazy中:

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

Usually you should wrap in Lazy the expansions of heads in HList and Coproduct rules, and also the expansion of Repr in Generic rules. 通常你应该在包装Lazy的头扩展中HListCoproduct规则,也扩大ReprGeneric规则。 The latter is not necessary here, I think, because you'll necessary go through hcons rule, that already has Lazy , to get from one Group or Message to another). 我认为,后者在这里不是必需的,因为您将需要通过hcons规则(已经具有Lazy ,以从一个GroupMessage传递到另一个)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM