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

时间:2018-11-20 21:17:42

标签: scala shapeless hlist

这真是困扰我。我收到一个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 :(得分:1)

考虑扩展copy test_schema.test from 's3://company.test/tmp/append.csv.gz' iam_role 'arn:aws:iam::<rolenumber>/RedshiftCopyUnload' delimiter ',' gzip; 的过程:

  • 使用$('input[type=checkbox]').change(function() { //logic goes here }); 扩展Meta[Message1]时,编译器需要Meta[Message1]
  • 后来,当用message扩展Meta[Field3 :: Group1 :: HNil]时,它需要Meta[Group1]

编译器看到,在该分支中,它已经扩展了至少具有相同复杂度(即group中具有相同数量的元素)的类型构造函数Meta[Field1 :: Field2 :: HNil]。因此,假设此扩展分支会导致无限循环,并报告隐式发散。

要防止此行为,可以使用::。从文档中:

  

包装延迟计算的值。还在隐式期间规避循环   搜索或错误的隐式差异,如下所示,并保持   懒惰地对应的隐式值。

因此,要解决此问题,您可以将HList的扩展shapeless.Lazy包裹在Lazy中:

Head

通常,您应将hconsimplicit 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,才能从一个hconsLazy到另一个)。