Shapeless HList隐式解析——发散式隐式展开
Shapeless HList implicit resolution - diverging implicit expansion
这真让我烦恼。我收到 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]]
}
考虑扩展的过程Meta[Message1]
:
- 当用
message
扩展 Meta[Message1]
时,编译器需要 Meta[Field3 :: Group1 :: HNil]
- 稍后,当用
group
扩展 Meta[Group1]
时,它需要 Meta[Field1 :: Field2 :: HNil]
编译器发现,在这个分支中它已经扩展了至少相同复杂度的类型构造函数 ::
(即 HList
中具有相同数量的元素)。所以它假设,这个扩展分支导致无限循环,并报告隐式分歧。
要防止这种行为,您可以使用 shapeless.Lazy
。来自文档:
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.
因此,要解决此问题,您可以将 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
规则中的头部扩展,以及 Generic
规则中的 Repr
扩展.后者在这里不是必需的,我认为,因为你必须通过 hcons
规则,已经有 Lazy
,从一个 Group
或 Message
到另一个).
这真让我烦恼。我收到 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]]
}
考虑扩展的过程Meta[Message1]
:
- 当用
message
扩展Meta[Message1]
时,编译器需要Meta[Field3 :: Group1 :: HNil]
- 稍后,当用
group
扩展Meta[Group1]
时,它需要Meta[Field1 :: Field2 :: HNil]
编译器发现,在这个分支中它已经扩展了至少相同复杂度的类型构造函数 ::
(即 HList
中具有相同数量的元素)。所以它假设,这个扩展分支导致无限循环,并报告隐式分歧。
要防止这种行为,您可以使用 shapeless.Lazy
。来自文档:
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.
因此,要解决此问题,您可以将 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
规则中的头部扩展,以及 Generic
规则中的 Repr
扩展.后者在这里不是必需的,我认为,因为你必须通过 hcons
规则,已经有 Lazy
,从一个 Group
或 Message
到另一个).