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:
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
add a comment |
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
add a comment |
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
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
scala shapeless hlist
asked Nov 20 at 21:17
Cheetah
5,5162474138
5,5162474138
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
Consider the process of expanding Meta[Message1]
:
- When expanding
Meta[Message1]
withmessage
the compiler needsMeta[Field3 :: Group1 :: HNil]
- Later, when expanding
Meta[Group1]
withgroup
it needsMeta[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).
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 aLazy
on thet
param of thehcons
method.
– Cheetah
Nov 21 at 8:23
@Cheetah No, it's OK to leavet
withoutLazy
, 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 throughh
. In general you may needLazy
inp
ofgroup
andmessage
, because it may be possible forgroup
to recursively callgroup
again. Here it's not required, because with this definitiongroup
can callgroup
only through the head ofhcons
, which already hasLazy
.
– Kolmar
Nov 21 at 9:55
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Consider the process of expanding Meta[Message1]
:
- When expanding
Meta[Message1]
withmessage
the compiler needsMeta[Field3 :: Group1 :: HNil]
- Later, when expanding
Meta[Group1]
withgroup
it needsMeta[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).
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 aLazy
on thet
param of thehcons
method.
– Cheetah
Nov 21 at 8:23
@Cheetah No, it's OK to leavet
withoutLazy
, 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 throughh
. In general you may needLazy
inp
ofgroup
andmessage
, because it may be possible forgroup
to recursively callgroup
again. Here it's not required, because with this definitiongroup
can callgroup
only through the head ofhcons
, which already hasLazy
.
– Kolmar
Nov 21 at 9:55
add a comment |
Consider the process of expanding Meta[Message1]
:
- When expanding
Meta[Message1]
withmessage
the compiler needsMeta[Field3 :: Group1 :: HNil]
- Later, when expanding
Meta[Group1]
withgroup
it needsMeta[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).
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 aLazy
on thet
param of thehcons
method.
– Cheetah
Nov 21 at 8:23
@Cheetah No, it's OK to leavet
withoutLazy
, 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 throughh
. In general you may needLazy
inp
ofgroup
andmessage
, because it may be possible forgroup
to recursively callgroup
again. Here it's not required, because with this definitiongroup
can callgroup
only through the head ofhcons
, which already hasLazy
.
– Kolmar
Nov 21 at 9:55
add a comment |
Consider the process of expanding Meta[Message1]
:
- When expanding
Meta[Message1]
withmessage
the compiler needsMeta[Field3 :: Group1 :: HNil]
- Later, when expanding
Meta[Group1]
withgroup
it needsMeta[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).
Consider the process of expanding Meta[Message1]
:
- When expanding
Meta[Message1]
withmessage
the compiler needsMeta[Field3 :: Group1 :: HNil]
- Later, when expanding
Meta[Group1]
withgroup
it needsMeta[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).
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 aLazy
on thet
param of thehcons
method.
– Cheetah
Nov 21 at 8:23
@Cheetah No, it's OK to leavet
withoutLazy
, 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 throughh
. In general you may needLazy
inp
ofgroup
andmessage
, because it may be possible forgroup
to recursively callgroup
again. Here it's not required, because with this definitiongroup
can callgroup
only through the head ofhcons
, which already hasLazy
.
– Kolmar
Nov 21 at 9:55
add a comment |
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 aLazy
on thet
param of thehcons
method.
– Cheetah
Nov 21 at 8:23
@Cheetah No, it's OK to leavet
withoutLazy
, 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 throughh
. In general you may needLazy
inp
ofgroup
andmessage
, because it may be possible forgroup
to recursively callgroup
again. Here it's not required, because with this definitiongroup
can callgroup
only through the head ofhcons
, which already hasLazy
.
– 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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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