Shapeless HList implicit resolution - diverging implicit expansion












0














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:



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]]
}









share|improve this question



























    0














    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:



    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]]
    }









    share|improve this question

























      0












      0








      0







      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:



      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]]
      }









      share|improve this question













      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:



      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]]
      }






      scala shapeless hlist






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 20 at 21:17









      Cheetah

      5,5162474138




      5,5162474138
























          1 Answer
          1






          active

          oldest

          votes


















          1














          Consider the process of expanding Meta[Message1]:




          • When expanding Meta[Message1] with message the compiler needs Meta[Field3 :: Group1 :: HNil]

          • Later, when expanding Meta[Group1] with group it needs Meta[Field1 :: Field2 :: HNil]


          The compiler sees, that in this branch it has already expanded type constructor :: of at least the same complexity (i.e., with the same number of elements in the 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. 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:



          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. 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).






          share|improve this answer





















          • I've learnt something new here - thank you! So in the general scenario where I will have groups within groups, if I've understood you correctly I'll also need a Lazy on the t param of the hcons method.
            – Cheetah
            Nov 21 at 8:23










          • @Cheetah No, it's OK to leave t without Lazy, because the size of the tail is growing smaller with each recursion step, and the compiler is OK with following it to the end. You can get to a larger or same size hlist only through h. In general you may need Lazy in p of group and message, because it may be possible for group to recursively call group again. Here it's not required, because with this definition group can call group only through the head of hcons, which already has Lazy.
            – Kolmar
            Nov 21 at 9:55











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53401666%2fshapeless-hlist-implicit-resolution-diverging-implicit-expansion%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          Consider the process of expanding Meta[Message1]:




          • When expanding Meta[Message1] with message the compiler needs Meta[Field3 :: Group1 :: HNil]

          • Later, when expanding Meta[Group1] with group it needs Meta[Field1 :: Field2 :: HNil]


          The compiler sees, that in this branch it has already expanded type constructor :: of at least the same complexity (i.e., with the same number of elements in the 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. 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:



          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. 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).






          share|improve this answer





















          • I've learnt something new here - thank you! So in the general scenario where I will have groups within groups, if I've understood you correctly I'll also need a Lazy on the t param of the hcons method.
            – Cheetah
            Nov 21 at 8:23










          • @Cheetah No, it's OK to leave t without Lazy, because the size of the tail is growing smaller with each recursion step, and the compiler is OK with following it to the end. You can get to a larger or same size hlist only through h. In general you may need Lazy in p of group and message, because it may be possible for group to recursively call group again. Here it's not required, because with this definition group can call group only through the head of hcons, which already has Lazy.
            – Kolmar
            Nov 21 at 9:55
















          1














          Consider the process of expanding Meta[Message1]:




          • When expanding Meta[Message1] with message the compiler needs Meta[Field3 :: Group1 :: HNil]

          • Later, when expanding Meta[Group1] with group it needs Meta[Field1 :: Field2 :: HNil]


          The compiler sees, that in this branch it has already expanded type constructor :: of at least the same complexity (i.e., with the same number of elements in the 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. 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:



          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. 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).






          share|improve this answer





















          • I've learnt something new here - thank you! So in the general scenario where I will have groups within groups, if I've understood you correctly I'll also need a Lazy on the t param of the hcons method.
            – Cheetah
            Nov 21 at 8:23










          • @Cheetah No, it's OK to leave t without Lazy, because the size of the tail is growing smaller with each recursion step, and the compiler is OK with following it to the end. You can get to a larger or same size hlist only through h. In general you may need Lazy in p of group and message, because it may be possible for group to recursively call group again. Here it's not required, because with this definition group can call group only through the head of hcons, which already has Lazy.
            – Kolmar
            Nov 21 at 9:55














          1












          1








          1






          Consider the process of expanding Meta[Message1]:




          • When expanding Meta[Message1] with message the compiler needs Meta[Field3 :: Group1 :: HNil]

          • Later, when expanding Meta[Group1] with group it needs Meta[Field1 :: Field2 :: HNil]


          The compiler sees, that in this branch it has already expanded type constructor :: of at least the same complexity (i.e., with the same number of elements in the 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. 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:



          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. 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).






          share|improve this answer












          Consider the process of expanding Meta[Message1]:




          • When expanding Meta[Message1] with message the compiler needs Meta[Field3 :: Group1 :: HNil]

          • Later, when expanding Meta[Group1] with group it needs Meta[Field1 :: Field2 :: HNil]


          The compiler sees, that in this branch it has already expanded type constructor :: of at least the same complexity (i.e., with the same number of elements in the 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. 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:



          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. 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).







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 20 at 23:19









          Kolmar

          11.4k11317




          11.4k11317












          • I've learnt something new here - thank you! So in the general scenario where I will have groups within groups, if I've understood you correctly I'll also need a Lazy on the t param of the hcons method.
            – Cheetah
            Nov 21 at 8:23










          • @Cheetah No, it's OK to leave t without Lazy, because the size of the tail is growing smaller with each recursion step, and the compiler is OK with following it to the end. You can get to a larger or same size hlist only through h. In general you may need Lazy in p of group and message, because it may be possible for group to recursively call group again. Here it's not required, because with this definition group can call group only through the head of hcons, which already has Lazy.
            – Kolmar
            Nov 21 at 9:55


















          • I've learnt something new here - thank you! So in the general scenario where I will have groups within groups, if I've understood you correctly I'll also need a Lazy on the t param of the hcons method.
            – Cheetah
            Nov 21 at 8:23










          • @Cheetah No, it's OK to leave t without Lazy, because the size of the tail is growing smaller with each recursion step, and the compiler is OK with following it to the end. You can get to a larger or same size hlist only through h. In general you may need Lazy in p of group and message, because it may be possible for group to recursively call group again. Here it's not required, because with this definition group can call group only through the head of hcons, which already has Lazy.
            – Kolmar
            Nov 21 at 9:55
















          I've learnt something new here - thank you! So in the general scenario where I will have groups within groups, if I've understood you correctly I'll also need a Lazy on the t param of the hcons method.
          – Cheetah
          Nov 21 at 8:23




          I've learnt something new here - thank you! So in the general scenario where I will have groups within groups, if I've understood you correctly I'll also need a Lazy on the t param of the hcons method.
          – Cheetah
          Nov 21 at 8:23












          @Cheetah No, it's OK to leave t without Lazy, because the size of the tail is growing smaller with each recursion step, and the compiler is OK with following it to the end. You can get to a larger or same size hlist only through h. In general you may need Lazy in p of group and message, because it may be possible for group to recursively call group again. Here it's not required, because with this definition group can call group only through the head of hcons, which already has Lazy.
          – Kolmar
          Nov 21 at 9:55




          @Cheetah No, it's OK to leave t without Lazy, because the size of the tail is growing smaller with each recursion step, and the compiler is OK with following it to the end. You can get to a larger or same size hlist only through h. In general you may need Lazy in p of group and message, because it may be possible for group to recursively call group again. Here it's not required, because with this definition group can call group only through the head of hcons, which already has Lazy.
          – Kolmar
          Nov 21 at 9:55


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53401666%2fshapeless-hlist-implicit-resolution-diverging-implicit-expansion%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          404 Error Contact Form 7 ajax form submitting

          How to know if a Active Directory user can login interactively

          TypeError: fit_transform() missing 1 required positional argument: 'X'